Python: il modo migliore per aggiungere a sys.path rispetto allo script corrente in esecuzione


99

Ho una directory piena di script (diciamo project/bin). Ho anche una libreria situata in project/libe voglio che gli script la caricino automaticamente. Questo è quello che normalmente uso all'inizio di ogni script:

#!/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

Questo è un po 'ingombrante, brutto e deve essere incollato all'inizio di ogni file. C'è un modo migliore per farlo?

Quello che spero in realtà è qualcosa di così fluido:

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

import mylib

O ancora meglio, qualcosa che non si interromperà quando il mio editor (o qualcun altro che ha accesso con commit) decide di riordinare le importazioni come parte del suo processo di pulizia:

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

Ciò non sarebbe portato direttamente su piattaforme non posix, ma manterrebbe le cose pulite.


Risposte:


26

Se non vuoi modificare ogni file

  • Installa la tua libreria come una normale libreria Python
    o
  • Imposta il PYTHONPATHtuolib

o se si desidera aggiungere una singola riga a ogni file, aggiungere una dichiarazione di importazione all'inizio, ad es

import import_my_lib

tieni import_my_lib.pyin bin e import_my_libpuoi impostare correttamente il percorso di python su quello libche vuoi


118

Questo è quello che uso:

import os, sys
sys.path.append(os.path.join(os.path.dirname(__file__), "lib"))

2
Vorrei fare sys.path.insert (0, ..) in modo che non venga sovrascritto da altri percorsi.
John Jiang

Non c'è davvero alcun modo per farlo funzionare automaticamente?
Denis de Bernardy

1
Questo è leggermente rischioso. Se __file__è un nome di file relativo relativo alla directory di lavoro corrente (ad esempio, setup.py), os.path.dirname(__file__)sarà la stringa vuota. Per questo e simili preoccupazioni sollevate da John Jiang , ekhumoro 'il più soluzione general-purpose è fortemente preferibile.
Cecil Curry

29

Sto usando:

import sys,os
sys.path.append(os.getcwd())

5
Lo faccio spesso e bastasys.path.append('.')
kimbo

1
cosa succede se lo script viene eseguito da una directory diversa? ad esempio dalla directory principale fornendo il percorso completo del sistema? quindi os.getcwd () restituirà "/"
obayhan

2
Non farlo mai. La directory di lavoro corrente (CWD) è non è garantito per essere ciò che si pensa che è - specialmente sotto i casi limite imprevedibili è sicuramente dovrebbe avete visto venire un miglio di distanza. Basta fare riferimento __file__invece come qualsiasi sviluppatore quasi sano di mente.
Cecil Curry

14

Crea un modulo wrapper project/bin/lib, che contiene questo:

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

Quindi puoi sostituire tutto il cruft nella parte superiore dei tuoi script con:

#!/usr/bin/python
from lib import mylib

9

Se non desideri modificare il contenuto dello script in alcun modo, anteponi la directory di lavoro corrente .a $ PYTHONPATH (vedi esempio sotto)

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

E chiamalo un giorno!


docs.python.org/3/tutorial/modules.html#the-module-search-path dice: "sys.path è inizializzato da queste posizioni: -La directory contenente lo script di input". Penso che questo non sia vero.

Tendo a farlo, specialmente in .envrc in modo che con direnv sia anche automatico e isolato.
EdvardM

8

Usare python 3.4+
Escludendo l'uso di cx_freeze o usando in IDLE. 😃

import sys
from pathlib import Path

sys.path.append(Path(__file__).parent / "lib")

Compatibile con Python2: import pathlib import os sys.path.append (os.path.dirname ( file ))
michael

5

È possibile eseguire lo script con python -mdalla directory root pertinente. E passa il "percorso dei moduli" come argomento.

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


Un altro esempio:

$ 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

1
L'uso dello switch m è il modo consigliato per farlo e non gli hack del percorso sys
Mr_and_Mrs_D

4

C'è un problema con ogni risposta fornita che può essere riassunto come "aggiungi questo magico incantesimo all'inizio del tuo script. Guarda cosa puoi fare con solo una o due righe di codice". Non funzioneranno in ogni situazione possibile!

Ad esempio, uno di questi incantesimi magici utilizza file . Sfortunatamente, se impacchettate il vostro script usando cx_Freeze o state usando IDLE, questo risulterà in un'eccezione.

Un altro di questi incantesimi magici usa os.getcwd (). Funzionerà solo se si esegue lo script dal prompt dei comandi e la directory contenente lo script è la directory di lavoro corrente (ovvero è stato utilizzato il comando cd per passare alla directory prima di eseguire lo script). Eh dei! Spero di non dover spiegare perché questo non funzionerà se il tuo script Python è nel PERCORSO da qualche parte e lo hai eseguito semplicemente digitando il nome del tuo file di script.

Fortunatamente, c'è un incantesimo magico che funzionerà in tutti i casi che ho testato. Sfortunatamente, l'incantesimo magico è più di una o due righe di codice.

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)

Come puoi vedere, non è un compito facile!


0

Questo funziona meglio per me. Uso:

os.path.abspath('')

Su Mac dovrebbe stampare qualcosa come:

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

Per ottenere il percorso addominale al wd corrente, questo è meglio perché ora puoi salire se vuoi, in questo modo:

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

E adesso:

 '/Users/<your username>/'

Quindi, se vuoi importare utilsda qui, '/Users/<your username>/'
tutto ciò che ti resta da fare è:

import sys
sys.path.append(os.path.abspath('../'))

0

Vedo una faccenda nel tuo esempio. Se stai eseguendo i tuoi script bin come ./bin/foo.py, invece che python ./bin/foo.py, c'è un'opzione di usare lo shebang per cambiare la $PYTHONPATHvariabile.

Tuttavia, non puoi modificare le variabili di ambiente direttamente in Shebangs, quindi avrai bisogno di un piccolo script di supporto. Mettilo python.shnella tua bincartella:

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

E poi cambia il tuo modo ./bin/foo.pydi essere#!bin/python.sh


0

Quando proviamo a eseguire il file python con percorso dal terminale.

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)

Salva il file (test.py).

Sistema di corsa.

Apri il terminale e vai alla directory in cui si trova il file di salvataggio. allora scrivi

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

Premi invio

Produzione:

File Name: test, Argument dir: /home/saiful/Desktop/bird.jpg

0

Io uso:

from site import addsitedir

Quindi, puoi usare qualsiasi directory relativa! addsitedir('..\lib') ; i due punti indicano prima lo spostamento (su) di una directory.

Ricorda che tutto dipende dalla tua directory di lavoro corrente da cui parti. Se C: \ Joe \ Jen \ Becky, allora additedir ('.. \ lib') importa nel percorso C: \ Joe \ Jen \ lib

C:\
  |__Joe
      |_ Jen
      |     |_ Becky
      |_ lib

Apprezzo la risposta, ma questo non aggiunge un percorso relativo allo script corrente in esecuzione. Aggiunge un percorso relativo alla directory di lavoro corrente
James Harr,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.