Python: najlepszy sposób dodawania do sys.path względem aktualnie uruchomionego skryptu

102

Mam katalog pełen skryptów (powiedzmy project/bin). Mam również bibliotekę znajdującą się w project/libi chcę, aby skrypty automatycznie ją ładowały. Oto, czego zwykle używam u góry każdego skryptu:

#!/usr/bin/python
from os.path import dirname, realpath, sep, pardir
import sys
sys.path.append(dirname(realpath(__file__)) + sep + pardir + sep + "lib")

# ... now the real code
import mylib

Jest to trochę kłopotliwe, brzydkie i trzeba je wkleić na początku każdego pliku. Czy jest lepszy sposób, aby to zrobić?

Naprawdę liczę na coś tak gładkiego, jak to:

#!/usr/bin/python
import sys.path
from os.path import pardir, sep
sys.path.append_relative(pardir + sep + "lib")

import mylib

Lub jeszcze lepiej, coś, co nie zepsuje się, gdy mój redaktor (lub ktoś inny, kto ma dostęp do zatwierdzania) zdecyduje się zmienić kolejność importu w ramach procesu czyszczenia:

#!/usr/bin/python --relpath_append ../lib
import mylib

To nie przenosi się bezpośrednio na platformy inne niż POSIX, ale utrzymałoby wszystko w czystości.

James Harr
źródło
1
Zobacz też: stackoverflow.com/questions/2349991/…
dreftymac

Odpowiedzi:

25

Jeśli nie chcesz edytować każdego pliku

  • Zainstaluj swoją bibliotekę jak zwykłą bibliotekę Pythona
    lub
  • Ustaw PYTHONPATHna swójlib

lub jeśli chcesz dodać jedną linię do każdego pliku, dodaj u góry instrukcję importu, np

import import_my_lib

zachowaj import_my_lib.pyw bin i import_my_libmożesz poprawnie ustawić ścieżkę Pythona na cokolwiek libchcesz

Anurag Uniyal
źródło
119

Oto czego używam:

import os, sys
sys.path.append(os.path.join(os.path.dirname(__file__), "lib"))
jterrace
źródło
2
Zrobiłbym sys.path.insert (0, ..), aby nie został zastąpiony przez inne ścieżki.
John Jiang
Czy naprawdę nie ma sposobu, aby uruchomić to automatycznie?
Denis de Bernardy
1
To jest trochę ryzykowne. Jeśli __file__jest względną nazwą pliku względem bieżącego katalogu roboczego (np. setup.py), To os.path.dirname(__file__)będzie to pusty ciąg. Z tego i podobnych problemów podniesiony przez Jana Jiang , ekhumoro „s więcej rozwiązaniem ogólnego przeznaczenia jest wysoce korzystne.
Cecil Curry
31

Używam:

import sys,os
sys.path.append(os.getcwd())
DusX
źródło
5
Często po prostu to robięsys.path.append('.')
kimbo
1
co jeśli skrypt jest uruchamiany z innego katalogu? na przykład z katalogu głównego, podając pełną ścieżkę systemową? wtedy os.getcwd () zwróci „/”
obayhan
2
Nigdy tego nie rób. Bieżący katalog roboczy (CWD) jest nie gwarancją, co myślisz to jest - zwłaszcza w nieprzewidzianych przypadkach krawędziowych na pewno powinien widzieli przyjście na milę. __file__Zamiast tego po prostu odwołaj się, jak każdy quasi-rozsądny programista.
Cecil Curry
15

Utwórz moduł opakowujący project/bin/lib, który zawiera:

import sys, os

sys.path.insert(0, os.path.join(
    os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'lib'))

import mylib

del sys.path[0], sys, os

Następnie możesz zamienić wszystkie cruft na górze swoich skryptów na:

#!/usr/bin/python
from lib import mylib
ekhumoro
źródło
9

Jeśli nie chcesz w żaden sposób zmieniać zawartości skryptu, dołącz bieżący katalog roboczy .do $ PYTHONPATH (patrz przykład poniżej)

PYTHONPATH=.:$PYTHONPATH alembic revision --autogenerate -m "First revision"

I nazwij to dniem!

Khanh Hua
źródło
Docs.python.org/3/tutorial/modules.html#the-module-search-path mówi: „sys.path jest inicjowany z tych lokalizacji: -Katalog zawierający skrypt wejściowy”. Myślę, że to nieprawda.
Zwykle robię to, zwłaszcza w .envrc, więc z direnv jest to nawet automatyczne i izolowane.
EdvardM,
8

Korzystanie z Pythona 3.4+
Zakaz używania cx_freeze lub używania w IDLE. 😃

import sys
from pathlib import Path

sys.path.append(Path(__file__).parent / "lib")
GollyJer
źródło
Kompatybilny z Python2: import pathlib import os sys.path.append (os.path.dirname ( file ))
michael
6

Możesz uruchomić skrypt z python -modpowiedniego katalogu głównego. I przekaż „ścieżkę modułów” jako argument.

Przykład: $ python -m module.sub_module.main # Notice there is no '.py' at the end.


Inny przykład:

$ tree  # Given this file structure
.
├── bar
│   ├── __init__.py
│   └── mod.py
└── foo
    ├── __init__.py
    └── main.py

$ cat foo/main.py
from bar.mod import print1
print1()

$ cat bar/mod.py
def print1():
    print('In bar/mod.py')

$ python foo/main.py  # This gives an error
Traceback (most recent call last):
  File "foo/main.py", line 1, in <module>
    from bar.mod import print1
ImportError: No module named bar.mod

$ python -m foo.main  # But this succeeds
In bar/mod.py
Eyal Levin
źródło
2
Używanie przełącznika m jest zalecanym sposobem, a nie hackami ścieżki sys
Mr_and_Mrs_D
4

Z każdą podaną odpowiedzią występuje problem, który można podsumować słowami „po prostu dodaj to magiczne zaklęcie na początku swojego skryptu. Zobacz, co możesz zrobić, używając tylko jednego lub dwóch wierszy kodu”. Nie sprawdzą się w każdej możliwej sytuacji!

Na przykład jedno takie magiczne zaklęcie używa pliku . Niestety, jeśli spakujesz swój skrypt za pomocą cx_Freeze lub używasz IDLE, spowoduje to wyjątek.

Kolejne takie magiczne zaklęcie używa os.getcwd (). To zadziała tylko wtedy, gdy uruchamiasz skrypt z wiersza poleceń, a katalog zawierający skrypt jest bieżącym katalogiem roboczym (to znaczy, że przed uruchomieniem skryptu użyto polecenia cd, aby przejść do katalogu). Ech bogowie! Mam nadzieję, że nie muszę wyjaśniać, dlaczego to nie zadziała, jeśli twój skrypt Pythona jest gdzieś w PATH i uruchomiłeś go, po prostu wpisując nazwę swojego pliku skryptu.

Na szczęście istnieje magiczna inkantacja, która zadziała we wszystkich testowanych przeze mnie przypadkach. Niestety, magiczne zaklęcie to coś więcej niż tylko jedna lub dwie linijki kodu.

import inspect
import os
import sys

# Add script directory to sys.path.
# This is complicated due to the fact that __file__ is not always defined.

def GetScriptDirectory():
    if hasattr(GetScriptDirectory, "dir"):
        return GetScriptDirectory.dir
    module_path = ""
    try:
        # The easy way. Just use __file__.
        # Unfortunately, __file__ is not available when cx_freeze is used or in IDLE.
        module_path = __file__
    except NameError:
        if len(sys.argv) > 0 and len(sys.argv[0]) > 0 and os.path.isabs(sys.argv[0]):
            module_path = sys.argv[0]
        else:
            module_path = os.path.abspath(inspect.getfile(GetScriptDirectory))
            if not os.path.exists(module_path):
                # If cx_freeze is used the value of the module_path variable at this point is in the following format.
                # {PathToExeFile}\{NameOfPythonSourceFile}. This makes it necessary to strip off the file name to get the correct
                # path.
                module_path = os.path.dirname(module_path)
    GetScriptDirectory.dir = os.path.dirname(module_path)
    return GetScriptDirectory.dir

sys.path.append(os.path.join(GetScriptDirectory(), "lib"))
print(GetScriptDirectory())
print(sys.path)

Jak widać, nie jest to łatwe zadanie!

Ben Key
źródło
1

Ten działa najlepiej dla mnie. Posługiwać się:

os.path.abspath('')

Na Macu powinien wydrukować coś takiego:

'/Users/<your username>/<path_to_where_you_at>'

Aby uzyskać ścieżkę abs do bieżącego wd, ten jest lepszy, ponieważ teraz możesz przejść w górę, jeśli chcesz, na przykład:

os.path.abspath('../')

I teraz:

 '/Users/<your username>/'

Więc jeśli chcesz zaimportować utilsz tego miejsca '/Users/<your username>/'
, pozostało Ci tylko:

import sys
sys.path.append(os.path.abspath('../'))
Kohn1001
źródło
0

W twoim przykładzie widzę shebang. Jeśli uruchamiasz skrypty bin jako ./bin/foo.pyzamiast python ./bin/foo.py, istnieje opcja użycia shebang do zmiany $PYTHONPATHzmiennej.

Nie możesz jednak zmieniać zmiennych środowiskowych bezpośrednio w shebangs, więc będziesz potrzebować małego skryptu pomocniczego. Umieść to python.shw swoim binfolderze:

#!/usr/bin/env bash
export PYTHONPATH=$PWD/lib
exec "/usr/bin/python" "$@"

A następnie zmienić shebang twoich ./bin/foo.pybyć#!bin/python.sh

imbolc
źródło
0

Kiedy próbujemy uruchomić plik Pythona ze ścieżką z terminala.

import sys
#For file name
file_name=sys.argv[0]
#For first argument
dir= sys.argv[1]
print("File Name: {}, argument dir: {}".format(file_name, dir)

Zapisz plik (test.py).

Działający system.

Otwórz terminal i przejdź do katalogu, w którym jest zapisywany plik. następnie napisz

python test.py "/home/saiful/Desktop/bird.jpg"

Wciśnij Enter

Wynik:

File Name: test, Argument dir: /home/saiful/Desktop/bird.jpg
wspaniały islam
źródło
0

Używam:

from site import addsitedir

Następnie można użyć dowolnego katalogu względnego! addsitedir('..\lib') ; dwie kropki oznaczają, że najpierw przenieś (w górę) jeden katalog.

Pamiętaj, że wszystko zależy od tego, z jakiego katalogu roboczego zaczynasz. Jeśli C: \ Joe \ Jen \ Becky, a następnie dodaje importitedir ('.. \ lib') do ścieżki C: \ Joe \ Jen \ lib

C:\
  |__Joe
      |_ Jen
      |     |_ Becky
      |_ lib
user7693644
źródło
Doceniam odpowiedź, ale nie dodaje to ścieżki względnej do aktualnie uruchomionego skryptu. Dodaje ścieżkę względną do bieżącego katalogu roboczego
James Harr