Czy możesz mi powiedzieć, jak mogę odczytać plik znajdujący się w moim pakiecie Python?
Moja sytuacja
Pakiet, który ładuję, ma wiele szablonów (plików tekstowych używanych jako ciągi znaków), które chcę załadować z poziomu programu. Ale jak określić ścieżkę do takiego pliku?
Wyobraź sobie, że chcę przeczytać plik z:
package\templates\temp_file
Jakaś manipulacja ścieżką? Śledzenie ścieżki bazowej pakietu?
Odpowiedzi:
[dodano 15.06.2016: najwyraźniej to nie działa we wszystkich sytuacjach. proszę zapoznać się z innymi odpowiedziami]
źródło
TLDR; Użyj
importlib.resources
modułu biblioteki standardów, jak wyjaśniono w metodzie nr 2 poniżej.Tradycyjny
pkg_resources
zsetuptools
nie jest już zalecane, ponieważ nowy sposób:setuptools
), ale polegasz tylko na standardowej bibliotece Pythona.Zachowałem tradycyjną listę jako pierwszą, aby wyjaśnić różnice w nowej metodzie podczas przenoszenia istniejącego kodu (portowanie również zostało wyjaśnione tutaj ).
Załóżmy, że Twoje szablony znajdują się w folderze zagnieżdżonym w pakiecie Twojego modułu:
1) Korzystanie
pkg_resources
zsetuptools
(wolno)Możesz użyć
pkg_resources
pakietu z dystrybucji setuptools , ale wiąże się to z kosztami i wydajnością :... i zauważ, że zgodnie z Setuptools /
pkg_resources
docs, nie powinieneś używaćos.path.join
:2) Python> = 3.7 lub używając wstecznej
importlib_resources
bibliotekiUżyj
importlib.resources
modułu biblioteki standardowej, który jest bardziej wydajny niżsetuptools
powyżej:Na przykład zadany w pytaniu musimy teraz:
<your_package>/templates/
w odpowiedni pakiet, tworząc w nim pusty__init__.py
plik,import
instrukcji ( koniec z analizowaniem nazw pakietów / modułów),resource_name = "temp_file"
(bez ścieżki).źródło
NotImplementedError: Can't perform this operation for loaders without 'get_data()'
jakieś pomysły?importlib.resources
ipkg_resources
to nie koniecznie zgodna .importlib.resources
działa z dodanymi plikami zipsys.path
, setuptools ipkg_resources
współpracuje z plikami egg, które są plikami zip przechowywanymi w katalogu, do którego sam jest dodawanysys.path
. Np. Zsys.path = [..., '.../foo', '.../bar.zip']
jajami.../foo
, ale paczkibar.zip
mogą być również importowane. Nie możesz użyćpkg_resources
do wyodrębnienia danych z pakietów wbar.zip
. Nie sprawdziłem, czy setuptools rejestruje program ładujący niezbędnyimportlib.resources
do pracy z jajami.Package has no location
pojawi się błąd ?templates
na przykładzie), możesz ustawićpackage
argument na__package__
, np.pkg_resources.read_text(__package__, 'temp_file')
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:
Należy przekazać
include_package_data=True
dosetup()
rozmowy. Plik manifestu jest potrzebny tylko wtedy, gdy chcesz używać setuptools / distutils i kompilować dystrybucje źródła. Aby upewnić się, żetemplates/temp_file
zostanie spakowany dla tej przykładowej struktury projektu, dodaj wiersz podobny do tego w pliku manifestu: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 maszsetup.py
pliku, możesz zignorować wszystkie rzeczyMANIFEST.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: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:
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:
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:
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 plikutemplates/__init__.py
, aby pliki danych znajdowały się w pakiecie podrzędnym, a nie w podkatalogu.package/templates
Ujawni również podkatalog jako samodzielnypackage.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 wmy_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:
Więcej informacji można znaleźć pod adresem https://github.com/wimglenn/resources-example .
źródło
importlib.resources
pomimo tych wszystkich niedociągnięć z niekompletnym interfejsem API, który już oczekuje na wycofanie ? Nowsze niekoniecznie znaczy lepsze. Powiedz mi, jakie zalety faktycznie oferuje w porównaniu ze standardowym pakietem pkgutil, o którym w Twojej odpowiedzi nie ma żadnej wzmianki?pkgutil.get_data()
potwierdzonego przeczucia - to niedopracowany interfejs API, który ma być przestarzały. To powiedziawszy, zgadzam się z tobą,importlib.resources
nie jest dużo lepszą alternatywą, ale dopóki PY3.10 nie rozwiąże tego problemu, stoję przy tym wyborze, heving dowiedział się, że nie jest to kolejny „standard” zalecany przez dokumentację.pkgutil
nie jest w ogóle wymieniony w harmonogramie wycofywania PEP 594 - Usuwanie rozładowanych baterii ze standardowej biblioteki i jest mało prawdopodobne, aby został usunięty bez uzasadnionego powodu. Istnieje od Pythona 2.3 i jest określony jako część protokołu modułu ładującego w PEP 302 . Używanie "niedefiniowalnego API" nie jest zbyt przekonującą odpowiedzią, która mogłaby opisać większość standardowej biblioteki Pythona!pkgutil
pod każdym względem. Twoje „przeczucie” i apel do autorytetu są dla mnie bez znaczenia, jeśli występują problemy zget_data
ładowaczami, pokaż dowody i praktyczne przykłady.Jeśli masz taką strukturę
potrzebujesz tego kodu:
Dziwna część „zawsze używaj ukośnika” pochodzi z
setuptools
interfejsów APIJeśli zastanawiasz się, gdzie jest dokumentacja:
źródło
pkg_resources
ma koszty ogólne, którepkgutil
przezwyciężają. Ponadto, jeśli podany kod jest uruchamiany jako punkt wejścia,__name__
zostanie oceniony na__main__
, a nie na nazwę pakietu.Treść w „10.8. Reading Datafiles Within a Package” w książce Python Cookbook, wydanie trzecie autorstwa Davida Beazleya i Briana K. Jonesa, udzielających odpowiedzi.
Po prostu przekażę to tutaj:
Załóżmy, że masz pakiet z plikami zorganizowanymi w następujący sposób:
Teraz przypuśćmy, że plik spam.py chce odczytać zawartość pliku somedata.dat. Aby to zrobić, użyj następującego kodu:
Wynikowe dane zmiennej będą ciągiem bajtów zawierającym nieprzetworzoną zawartość pliku.
Pierwszym argumentem metody get_data () jest ciąg znaków zawierający nazwę pakietu. Możesz podać go bezpośrednio lub użyć specjalnej zmiennej, takiej jak
__package__
. Drugi argument to względna nazwa pliku w pakiecie. Jeśli to konieczne, możesz przejść do różnych katalogów, używając standardowych konwencji nazw plików Uniksa, o ile ostateczny katalog nadal znajduje się w pakiecie.W ten sposób pakiet można zainstalować jako katalog, .zip lub .egg.
źródło
Zaakceptowaną odpowiedzią powinno być użycie
importlib.resources
.pkgutil.get_data
wymaga również, aby argumentpackage
był pakietem innym niż przestrzeń nazw ( zobacz dokumentację pkgutil ). Dlatego katalog zawierający zasób musi mieć__init__.py
plik, dzięki czemu ma dokładnie takie same ograniczenia jakimportlib.resources
. Jeśli kwestia kosztów ogólnychpkg_resources
nie stanowi problemu, jest to również akceptowalna alternatywa.źródło
Każdy moduł Pythona w twoim pakiecie ma
__file__
atrybutMożesz go używać jako:
Aby uzyskać zasoby dotyczące jaj, zobacz: http://peak.telecommunity.com/DevCenter/PythonEggs#accessing-package-resources
źródło
zakładając, że używasz pilnika jajecznego; nie wyodrębniony:
„Rozwiązałem” to w ostatnim projekcie, używając skryptu poinstalacyjnego, który wyodrębnia moje szablony z jajka (pliku zip) do odpowiedniego katalogu w systemie plików. Było to najszybsze i najbardziej niezawodne rozwiązanie, jakie znalazłem, ponieważ
__path__[0]
czasami praca z nim może się nie udać (nie pamiętam nazwy, ale przeszedłem przez co najmniej jedną bibliotekę, która dodała coś przed tą listą!).Pliki z jajami są również zwykle pobierane w locie do tymczasowej lokalizacji zwanej „składnicą jaj”. Możesz zmienić tę lokalizację za pomocą zmiennej środowiskowej, przed uruchomieniem skryptu lub nawet później, np.
Istnieją jednak zasoby pkg_resources, które mogą wykonać zadanie poprawnie.
źródło