Wstęp do pakowania:
Zanim będziesz mógł się martwić czytaniem plików zasobów, pierwszym krokiem jest upewnienie się, że pliki danych są pakowane do Twojej dystrybucji - łatwo jest je odczytać bezpośrednio z drzewa źródłowego, ale ważną częścią jest upewnij się, że te pliki zasobów są dostępne z kodu w zainstalowanym pakiecie.
Zorganizuj swój projekt w ten sposób, umieszczając pliki danych w podkatalogu w pakiecie:
.
├── package
│ ├── __init__.py
│ ├── templates
│ │ └── temp_file
│ ├── mymodule1.py
│ └── mymodule2.py
├── README.rst
├── MANIFEST.in
└── setup.py
Należy przekazać include_package_data=True
do setup()
rozmowy. Plik manifestu jest potrzebny tylko wtedy, gdy chcesz używać setuptools / distutils i kompilować dystrybucje źródła. Aby upewnić się, że templates/temp_file
zostanie spakowany dla tej przykładowej struktury projektu, dodaj wiersz podobny do tego w pliku manifestu:
recursive-include package *
Notatka historyczna: Używanie pliku manifestu nie jest potrzebne w przypadku nowoczesnych backendów kompilacji, takich jak flit czy poezja, które domyślnie będą zawierać pliki danych pakietu. Tak więc, jeśli używasz pyproject.toml
i nie masz setup.py
pliku, możesz zignorować wszystkie rzeczy MANIFEST.in
.
Teraz, po usunięciu opakowania, w części do czytania ...
Rekomendacje:
Użyj standardowych pkgutil
interfejsów API biblioteki . W kodzie biblioteki będzie wyglądać tak:
# 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))
Działa w zamkach błyskawicznych. Działa na Pythonie 2 i Pythonie 3. Nie wymaga zależności innych firm. Nie znam żadnych wad (jeśli tak, to skomentuj odpowiedź).
Złe sposoby uniknięcia:
Zły sposób nr 1: użycie ścieżek względnych z pliku źródłowego
To jest obecnie akceptowana odpowiedź. W najlepszym przypadku wygląda to mniej więcej tak:
from pathlib import Path
resource_path = Path(__file__).parent / "templates"
data = resource_path.joinpath("temp_file").read_bytes()
print("data", repr(data))
Co z tym jest nie tak? Założenie, że masz dostępne pliki i podkatalogi, nie jest poprawne. To podejście nie działa, jeśli wykonuje się kod, który jest spakowany w zipie lub kółku i może być całkowicie poza kontrolą użytkownika, czy pakiet zostanie w ogóle wyodrębniony do systemu plików.
Zły sposób nr 2: używanie interfejsów API pkg_resources
Jest to opisane w odpowiedzi z największą liczbą głosów. Wygląda mniej więcej tak:
from pkg_resources import resource_string
data = resource_string(__name__, "templates/temp_file")
print("data", repr(data))
Co z tym jest nie tak? Dodaje zależność od środowiska wykonawczego do setuptools , która powinna być zależna tylko od czasu instalacji . Importowanie i używanie pkg_resources
może stać się naprawdę powolne, ponieważ kod tworzy działający zestaw wszystkich zainstalowanych pakietów, nawet jeśli interesowały Cię tylko własne zasoby pakietów. To nie jest wielka sprawa w czasie instalacji (ponieważ instalacja jest jednorazowa), ale jest brzydka w czasie wykonywania.
Zły sposób nr 3: używanie interfejsów API importlib.resources
To jest obecnie zalecenie w odpowiedzi z największą liczbą głosów. Jest to niedawny dodatek do biblioteki standardowej ( nowość w Pythonie 3.7 ), ale dostępny jest również backport. To wygląda tak:
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))
Co z tym jest nie tak? Cóż, niestety, to nie działa ... jeszcze. Jest to wciąż niepełny interfejs API, użycie importlib.resources
będzie wymagało dodania pustego pliku templates/__init__.py
, aby pliki danych znajdowały się w pakiecie podrzędnym, a nie w podkatalogu. package/templates
Ujawni również podkatalog jako samodzielny package.templates
pakiet podrzędny, który można zaimportować . Jeśli to nic wielkiego i ci to nie przeszkadza, możesz dodać __init__.py
tam plik i użyć systemu importu, aby uzyskać dostęp do zasobów. Jednak gdy już to robisz, równie dobrze możesz zamienić go w my_resources.py
plik i po prostu zdefiniować kilka bajtów lub zmiennych łańcuchowych w module, a następnie zaimportować je w kodzie Pythona. Tak czy inaczej, to system importu wykonuje tu ciężkie podnoszenie.
Przykładowy projekt:
Stworzyłem przykładowy projekt na github i załadowałem na PyPI , który demonstruje wszystkie cztery omówione powyżej podejścia. Wypróbuj z:
$ pip install resources-example
$ resources-example
Więcej informacji można znaleźć pod adresem https://github.com/wimglenn/resources-example .