Piszę pakiet Pythona z modułami, które muszą otwierać pliki danych w ./data/
podkatalogu. W tej chwili mam ścieżki do plików zakodowane na stałe w moich klasach i funkcjach. Chciałbym napisać solidniejszy kod, który będzie miał dostęp do podkatalogu niezależnie od tego, gdzie jest zainstalowany w systemie użytkownika.
Próbowałem różnych metod, ale jak dotąd nie miałem szczęścia. Wygląda na to, że większość poleceń „bieżącego katalogu” zwraca katalog systemowego interpretera języka Python, a nie katalog modułu.
Wydaje się, że powinien to być banalny, powszechny problem. Jednak nie mogę tego rozgryźć. Częścią problemu jest to, że moje pliki danych nie są .py
plikami, więc nie mogę używać funkcji importu i tym podobnych.
Jakieś sugestie?
W tej chwili mój katalog z pakietami wygląda następująco:
/
__init__.py
module1.py
module2.py
data/
data.txt
Próbuję uzyskać dostęp data.txt
z module*.py
!
__file__
nie działa z py2exe, gdy wartość będzie ścieżka do pliku zip.Standardowym sposobem na to jest użycie pakietów setuptools i pkg_resources.
Możesz ułożyć pakiet zgodnie z następującą hierarchią i skonfigurować plik instalacyjny pakietu tak, aby wskazywał mu zasoby danych, zgodnie z tym łączem:
http://docs.python.org/distutils/setupscript.html#installing-package-data
Następnie możesz ponownie znaleźć i użyć tych plików za pomocą pkg_resources, zgodnie z tym linkiem:
http://peak.telecommunity.com/DevCenter/PkgResources#basic-resource-access
import pkg_resources DATA_PATH = pkg_resources.resource_filename('<package name>', 'data/') DB_FILE = pkg_resources.resource_filename('<package name>', 'data/sqlite.db')
źródło
python-setuptools
tylko na tym? Jak dotąd__file__
działa dobrze dla mnie.from pkg_resources import resource_filename open(resource_filename('data', 'data.txt'), 'rb')
importlib.resources
zastępujepkg_resources
w tym celu (z powodu problemów z wydajnością).Aby zapewnić rozwiązanie działające dzisiaj. Zdecydowanie użyj tego interfejsu API, aby nie wymyślać na nowo wszystkich tych kół.
Wymagana jest prawdziwa nazwa pliku systemu plików. Jajka spakowane zostaną wyodrębnione do katalogu pamięci podręcznej:
from pkg_resources import resource_filename, Requirement path_to_vik_logo = resource_filename(Requirement.parse("enb.portals"), "enb/portals/reports/VIK_logo.png")
Zwraca czytelny obiekt podobny do pliku dla określonego zasobu; może to być rzeczywisty plik, StringIO lub podobny obiekt. Strumień jest w „trybie binarnym”, w tym sensie, że jakiekolwiek bajty w zasobie zostaną odczytane tak, jak są.
from pkg_resources import resource_stream, Requirement vik_logo_as_stream = resource_stream(Requirement.parse("enb.portals"), "enb/portals/reports/VIK_logo.png")
Wykrywanie pakietów i dostęp do zasobów przy użyciu pkg_resources
źródło
Często nie ma sensu udzielanie odpowiedzi, która szczegółowo opisuje kod, który nie działa tak, jak jest, ale uważam, że jest to wyjątek. Dodano Python 3.7,
importlib.resources
który ma zastąpićpkg_resources
. To działałoby w przypadku dostępu do plików w pakietach, które nie mają ukośników w nazwie, tjtj. możesz uzyskać dostęp do
data2.txt
wnętrza pakietufoo
na przykładimportlib.resources.open_binary('foo', 'data2.txt')
ale zakończy się niepowodzeniem z wyjątkiem
>>> importlib.resources.open_binary('foo', 'data/data.txt') Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib/python3.7/importlib/resources.py", line 87, in open_binary resource = _normalize_path(resource) File "/usr/lib/python3.7/importlib/resources.py", line 61, in _normalize_path raise ValueError('{!r} must be only a file name'.format(path)) ValueError: 'data/data2.txt' must be only a file name
Nie mogą być mocowane jedynie przez umieszczenie
__init__.py
wdata
, a następnie wykorzystanie go jako pakiet:importlib.resources.open_binary('foo.data', 'data.txt')
Przyczyną takiego zachowania jest „to jest zgodne z projektem” ; ale projekt może się zmienić ...
źródło
"This was a deliberate choice, but I think you have a valid use case. @brettcannon what do you think? And if we allow this, should we make sure it gets into Python 3.7?"
Potrzebujesz nazwy dla całego modułu, otrzymujesz drzewo katalogów, które nie zawiera tych szczegółów, dla mnie to zadziałało:
import pkg_resources print( pkg_resources.resource_filename(__name__, 'data/data.txt') )
Warto zauważyć, że setuptools nie wydaje się rozwiązywać plików na podstawie dopasowania nazwy do spakowanych plików danych, więc nie musisz dodawać
data/
przedrostka prawie bez względu na wszystko. Możesz użyć,os.path.join('data', 'data.txt)
jeśli potrzebujesz alternatywnych separatorów katalogów, jednak generalnie nie znajduję problemów ze zgodnością z zakodowanymi na stałe separatorami katalogów w stylu unixowym.źródło
Myślę, że znalazłem odpowiedź.
Tworzę moduł data_path.py, który importuję do innych moich modułów, zawierający:
data_path = os.path.join(os.path.dirname(__file__),'data')
A potem otwieram wszystkie moje pliki za pomocą
open(os.path.join(data_path,'filename'), <param>)
źródło
pkg_resources.resource_string('pkg_name', 'data/file.txt')
__file__
gdzieś użyłeś . W moim przypadku używam biblioteki, która naprawdę chce ścieżek, a nie strumieni. Oczywiście mogłem tymczasowo zapisać pliki na dysku, ale będąc leniwym, po prostu korzystam z funkcji setuptools.