Mam projekt w języku Python z plikiem konfiguracyjnym w katalogu głównym projektu. Plik konfiguracyjny musi być dostępny w kilku różnych plikach w całym projekcie.
Tak to wygląda mniej więcej tak: <ROOT>/configuration.conf
<ROOT>/A/a.py
, <ROOT>/A/B/b.py
(gdy b, a.py otworzyć plik konfiguracyjny).
Jaki jest najlepszy / najłatwiejszy sposób na uzyskanie ścieżki do katalogu głównego projektu i pliku konfiguracyjnego bez zależności od tego, w którym pliku w projekcie jestem? tj. bez używania ../../
? Można założyć, że znamy nazwę katalogu głównego projektu.
<ROOT>/__init__.py
istnieje?os.path.expanduser('~/.myproject/myproject.conf')
. Działa na systemach Unix i Windows.Odpowiedzi:
Możesz to zrobić tak, jak robi to Django: zdefiniuj zmienną w katalogu głównym projektu z pliku znajdującego się na najwyższym poziomie projektu. Na przykład, jeśli tak wygląda struktura projektu:
W
definitions.py
możesz zdefiniować (wymaga toimport os
):Tak więc, znając Project Root , możesz utworzyć zmienną wskazującą na lokalizację konfiguracji (można to zdefiniować w dowolnym miejscu, ale logicznym miejscem byłoby umieszczenie jej w miejscu, w którym zdefiniowane są stałe - np.
definitions.py
):Następnie można łatwo uzyskać dostęp do stałej (w każdym z pozostałych plików) za pomocą instrukcji import (np
utils.py
)from definitions import CONFIG_PATH
.źródło
__init__.py
plik również do katalogu głównego projektu? Czy to powinno być poprawne? Właśnie zacząłem od Pythona i nie wiem, jakie są najlepsze praktyki. Dzięki.__init__.py
nie będą wymagane, jako że plik jest wymagane tylko przy definiowaniu pakiety: Te__init__.py
pliki są wymagane, aby Python traktować jak katalogi zawierające pakiety; ma to na celu zapobieganie przypadkowemu ukryciu prawidłowych modułów, które pojawiają się później na ścieżce wyszukiwania modułów, przez katalogi o wspólnej nazwie, takiej jak łańcuch. W najprostszym przypadku__init__.py
może to być po prostu pusty plik, ale może również wykonać kod inicjujący dla pakietu lub ustawić__all__
zmienną, co zostanie opisane później. Zobacz: docs.python.org/3/tutorial/modules.html#packages__init.py__
pakietu głównego jest dopuszczalne, czy nie jest mile widziane . Oszczędziłoby to tworzenie kolejnego pliku, a także pozwoliłoby na ładniejszą składnięfrom root_pack import ROOT_DIR, CONFIG_PATH
.__init__.py
pusta, ale to nie jest do końca prawdą (w końcu jest to konwencja). Zobacz to więcej: stackoverflow.com/questions/2361124/using-init-pyos.path.abspath
dzwoni ciąg,'__file__'
. Przypomnij sobie, że w__file__
rzeczywistości jest to atrybut importu zdefiniowany dla modułów Pythona. W tym przypadku__file__
zwróci ścieżkę, z której moduł jest ładowany. Przeczytaj więcej tutaj (zobacz sekcję dotyczącą modułów): docs.python.org/3/reference/datamodel.htmlInne porady dotyczące używania pliku na najwyższym poziomie projektu. Nie jest to konieczne, jeśli używasz
pathlib.Path
iparent
(Python 3.4 i nowsze). Rozważ następującą strukturę katalogów, w której wszystkie pliki z wyjątkiemREADME.md
iutils.py
zostały pominięte.W
utils.py
definiujemy następującą funkcję.W dowolnym module w projekcie możemy teraz pobrać katalog główny projektu w następujący sposób.
Korzyści : Dowolny moduł, którego wywołania
get_project_root
można przenosić bez zmiany zachowania programu. Dopiero po przeniesieniu modułuutils.py
musimy zaktualizowaćget_project_root
i importować (można to zautomatyzować za pomocą narzędzi refaktoryzacyjnych).źródło
Wszystkie poprzednie rozwiązania wydają się być zbyt skomplikowane dla tego, czego myślę, że potrzebujesz i często nie działały dla mnie. Następujące jednowierszowe polecenie robi to, co chcesz:
źródło
Aby uzyskać ścieżkę do modułu „root”, możesz użyć:
Ale co ciekawsze, jeśli masz "obiekt" konfiguracyjny w swoim najwyższym module, możesz go odczytać w następujący sposób:
źródło
os
nie jest domyślnie dostępny. Musisz zaimportowaćos
. Więc dodanie liniiimport os
uczyniłoby odpowiedź bardziej kompletną.python3 -m topmodule.submodule.script
da/path/to/topmodule/submodule
zamiast/path/to/topmodule
.Standardowym sposobem osiągnięcia tego byłoby użycie
pkg_resources
modułu, który jest częściąsetuptools
pakietu.setuptools
służy do tworzenia instalowalnego pakietu języka Python.Możesz użyć,
pkg_resources
aby zwrócić zawartość żądanego pliku jako ciąg znaków i możesz użyć,pkg_resources
aby uzyskać rzeczywistą ścieżkę żądanego pliku w systemie.Powiedzmy, że masz pakiet o nazwie
stackoverflow
.Teraz powiedzmy, że chcesz uzyskać dostęp do pliku Rush z modułu
app.run
. Użyj,pkg_resources.resouces_filename
aby uzyskać ścieżkę do Rush ipkg_resources.resource_string
zdobyć zawartość Rush; w ten sposób:Wyjście:
Działa to dla wszystkich pakietów w twojej ścieżce Pythona. Więc jeśli chcesz wiedzieć, gdzie
lxml.etree
istnieje w twoim systemie:wynik:
Chodzi o to, że możesz użyć tej standardowej metody, aby uzyskać dostęp do plików, które są zainstalowane w twoim systemie (np. Pip install xxx lub yum -y install python-xxx) i plików, które są w module, nad którym aktualnie pracujesz.
źródło
Poniżej kodu Zwraca ścieżkę do katalogu głównego projektu
źródło
Próbować:
źródło
Z tym problemem też się zmagałem, aż doszedłem do tego rozwiązania. To moim zdaniem najczystsze rozwiązanie.
W swoim setup.py dodaj „pakiety”
W twoim python_script.py
źródło
python3 setup.py install
nim pakietu nie wskazywało już na folder kodu źródłowego, ale na znajdujące się w nim jajko~./virtualenv/..../app.egg
. Musiałem więc dołączyć plik konfiguracyjny do instalacji pakietu.Oto przykład: chcę uruchomić plik runio.py z poziomu helper1.py
Przykład drzewa projektu:
Pobierz katalog główny projektu:
Zbuduj ścieżkę do skryptu:
źródło
Pomogło mi to przy użyciu standardowego projektu PyCharm z moim środowiskiem wirtualnym (venv) w katalogu głównym projektu.
Poniższy kod nie jest najładniejszy, ale konsekwentnie pobiera katalog główny projektu. Zwraca pełną ścieżkę do katalogu venv z pliku
VIRTUAL_ENV
zmiennej środowiskowej, np/Users/NAME/documents/PROJECT/venv
Następnie na końcu dzieli ścieżkę
/
, tworząc tablicę z dwoma elementami. Pierwszym elementem będzie ścieżka projektu np/Users/NAME/documents/PROJECT
źródło
Ostatnio próbowałem zrobić coś podobnego i stwierdziłem, że te odpowiedzi nie są odpowiednie dla moich przypadków użycia (rozproszonej biblioteki, która musi wykryć root projektu). Przede wszystkim walczyłem w różnych środowiskach i platformach i nadal nie znalazłem czegoś idealnie uniwersalnego.
Kod lokalny dla projektu
Widziałem ten przykład wspomniany i używany w kilku miejscach, Django itp.
Jest to proste, działa tylko wtedy, gdy plik, w którym znajduje się fragment kodu, jest faktycznie częścią projektu. Nie pobieramy katalogu projektu, ale zamiast tego katalog fragmentu
Podobnie, podejście sys.modules psuje się, gdy jest wywoływane spoza punktu wejścia aplikacji, w szczególności zauważyłem, że wątek potomny nie może tego określić bez odniesienia do modułu „ głównego ”. Jawnie umieściłem import wewnątrz funkcji, aby zademonstrować import z wątku potomnego, przeniesienie go na najwyższy poziom app.py naprawiłoby to.
app.py
settings.py
Uruchomienie tego programu powoduje błąd atrybutu:
... stąd rozwiązanie oparte na wątkach
Lokalizacja niezależna
Używając tej samej struktury aplikacji co poprzednio, ale modyfikując settings.py
Podsumowanie: Najpierw chcemy dokładnie znaleźć identyfikator wątku głównego wątku. Jednak w Pythonie 3.4 + biblioteka wątków nie
threading.main_thread()
wszyscy używają wersji 3.4+, więc przeszukujemy wszystkie wątki szukając głównego wątku, zachowując jego identyfikator. Jeśli główny wątek został już zamknięty, nie będzie wymieniony wthreading.enumerate()
. PodnosimyRuntimeError()
w tej sprawie, dopóki nie znajdę lepszego rozwiązania.Następnie znajdujemy pierwszą ramkę stosu głównego wątku. Używając funkcji specyficznej dla cPythona
sys._current_frames()
, otrzymujemy słownik bieżącej ramki stosu każdego wątku. Następnie wykorzystującinspect.getouterframes()
możemy pobrać cały stos dla głównego wątku i pierwszej klatki. current_main_frame = sys._current_frames () [main_id] base_frame = inspect.getouterframes (current_main_frame) [- 1] Wreszcie, należy zająć się różnicami między implementacjami systemów Windows i Linuxinspect.getouterframes()
. Używając oczyszczonej nazwy plikuos.path.abspath()
ios.path.dirname()
wyczyść wszystko.Do tej pory testowałem to na Pythonie2.7 i 3.6 w systemie Windows oraz Python3.4 na WSL
źródło
Jeśli pracujesz z anaconda-project, możesz wysłać zapytanie do PROJECT_ROOT ze zmiennej środowiskowej -> os.getenv ('PROJECT_ROOT'). Działa to tylko wtedy, gdy skrypt jest wykonywany poprzez uruchomienie anaconda-project.
Jeśli nie chcesz, aby Twój skrypt był uruchamiany przez anaconda-project, możesz zapytać o bezwzględną ścieżkę wykonywalnego pliku binarnego interpretera języka Python, którego używasz, i wyodrębnić ciąg ścieżki do katalogu envs exclusive. Na przykład: interpreter języka Python w moim conda env znajduje się pod adresem:
Działa to tylko z projektem conda ze stałą strukturą projektu anakondy
źródło
Użyłem metody ../ do pobrania bieżącej ścieżki projektu.
Przykład: Project1 - D: \ projects
src
Pliki konfiguracyjne
Configuration.cfg
Path = "../ src / ConfigurationFiles / Configuration.cfg"
źródło
W chwili pisania tego tekstu żadne inne rozwiązania nie są bardzo samodzielne. Zależą one od zmiennej środowiskowej lub pozycji modułu w strukturze pakietu. Najlepsza odpowiedź z rozwiązaniem „Django” pada ofiarą tego ostatniego, ponieważ wymaga względnego importu. Ma również tę wadę, że trzeba modyfikować moduł na najwyższym poziomie.
Powinno to być poprawne podejście do znajdowania ścieżki katalogu pakietu najwyższego poziomu:
Działa poprzez pobranie pierwszego składnika z kropkowanego ciągu zawartego w nim
__name__
i użycie go jako klucza, wsys.modules
którym zwraca obiekt modułu z pakietu najwyższego poziomu. Jego__file__
atrybut zawiera ścieżkę, którą chcemy po przycięciu za/__init__.py
pomocąos.path.dirname()
.To rozwiązanie jest samowystarczalne. Działa w dowolnym miejscu w dowolnym module pakietu, w tym w
__init__.py
pliku najwyższego poziomu .źródło
Musiałem wdrożyć niestandardowe rozwiązanie, ponieważ nie jest to tak proste, jak mogłoby się wydawać. Moje rozwiązanie opiera się na inspekcji stosu śledzenia (
inspect.stack()
) +sys.path
i działa dobrze bez względu na lokalizację modułu Pythona, w którym funkcja jest wywoływana, ani interpretera (próbowałem, uruchamiając go w PyCharm, w powłoce poezji i innych ... ). Oto pełna realizacja z komentarzami:źródło
Jest tu wiele odpowiedzi, ale nie mogłem znaleźć czegoś prostego, który obejmowałby wszystkie przypadki, więc pozwól mi zasugerować również moje rozwiązanie:
źródło