Python: Cara terbaik untuk menambahkan ke sys.path yang terkait dengan skrip yang sedang berjalan

107

Saya memiliki direktori yang penuh dengan skrip (katakanlah project/bin). Saya juga memiliki perpustakaan yang terletak project/libdan ingin skrip memuatnya secara otomatis. Inilah yang biasanya saya gunakan di bagian atas setiap skrip:

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

Ini agak rumit, jelek, dan harus ditempel di awal setiap file. Apakah ada cara yang lebih baik untuk melakukan ini?

Sungguh apa yang saya harapkan adalah sesuatu yang sehalus ini:

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

import mylib

Atau bahkan lebih baik, sesuatu yang tidak akan rusak ketika editor saya (atau orang lain yang memiliki akses komit) memutuskan untuk menyusun ulang impor sebagai bagian dari proses pembersihannya:

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

Itu tidak akan melakukan port langsung ke platform non-posix, tetapi itu akan menjaga semuanya tetap bersih.

James Harr
sumber
1
Lihat juga: stackoverflow.com/questions/2349991/…
dreftymac

Jawaban:

26

Jika Anda tidak ingin mengedit setiap file

  • Instal pustaka Anda seperti pustaka python biasa
    atau
  • Setel PYTHONPATHkelib

atau jika Anda ingin menambahkan satu baris ke setiap file, tambahkan pernyataan import di atas misalnya

import import_my_lib

simpan import_my_lib.pydi bin dan import_my_libdapat dengan benar mengatur jalur python ke apa pun yang libAnda inginkan

Anurag Uniyal
sumber
124

Ini yang saya gunakan:

import os, sys
sys.path.append(os.path.join(os.path.dirname(__file__), "lib"))
jterrace
sumber
3
Saya akan melakukan sys.path.insert (0, ..) sehingga tidak ditimpa oleh jalur lain.
John Jiang
Apakah benar-benar tidak ada cara untuk menjalankan ini secara otomatis?
Denis de Bernardy
1
Ini sedikit berisiko. Jika __file__adalah nama file relatif terhadap direktori kerja saat ini (mis., setup.py), Maka os.path.dirname(__file__)akan menjadi string kosong. Untuk ini dan kekhawatiran serupa oleh John Jiang , ekhumoro 's lebih solusi untuk keperluan umum adalah sangat disukai.
Cecil Curry
30

Saya menggunakan:

import sys,os
sys.path.append(os.getcwd())
DusX
sumber
5
Saya sering melakukannyasys.path.append('.')
kimbo
2
bagaimana jika skrip dijalankan dari direktori yang berbeda? misalnya dari direktori root dengan memberikan jalur sistem lengkap? maka os.getcwd () akan mengembalikan "/"
obayhan
2
Jangan lakukan ini. Direktori kerja saat ini (CWD) tidak dijamin seperti yang Anda pikirkan - terutama dalam kasus yang tidak terduga yang pasti Anda lihat datang jauh. Hanya referensi __file__saja seperti pengembang yang semu.
Cecil Curry
16

Buat modul pembungkus project/bin/lib, yang berisi ini:

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

Kemudian Anda dapat mengganti semua cruft di bagian atas skrip Anda dengan:

#!/usr/bin/python
from lib import mylib
ekhumoro
sumber
9

Jika Anda tidak ingin mengubah konten skrip dengan cara apa pun, tambahkan direktori kerja saat ini .ke $ PYTHONPATH (lihat contoh di bawah)

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

Dan anggap saja itu sehari!

Khanh Hua
sumber
docs.python.org/3/tutorial/modules.html#the-module-search-path mengatakan: "sys.path diinisialisasi dari lokasi ini: -Direktori yang berisi skrip input". Saya pikir ini tidak benar.
Saya cenderung melakukan ini, terutama di .envrc sehingga dengan direnv yang otomatis dan terisolasi juga.
EdvardM
8

Menggunakan python 3.4+
Membatasi penggunaan cx_freeze atau menggunakan IDLE. 😃

import sys
from pathlib import Path

sys.path.append(Path(__file__).parent / "lib")
GollyJer
sumber
Kompatibel dengan Python2: import pathlib import os sys.path.append (os.path.dirname ( file ))
michael
6

Anda dapat menjalankan skrip dengan python -mdari direktori root yang relevan. Dan berikan "jalur modul" sebagai argumen.

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


Contoh lain:

$ 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
sumber
2
Menggunakan sakelar m adalah cara yang disarankan untuk melakukan ini dan bukan peretasan jalur sys
Mr_and_Mrs_D
4

Ada masalah dengan setiap jawaban yang diberikan yang dapat diringkas sebagai "tambahkan saja mantra ajaib ini ke awal skrip Anda. Lihat apa yang dapat Anda lakukan hanya dengan satu atau dua baris kode." Mereka tidak akan bekerja dalam setiap situasi yang memungkinkan!

Misalnya, salah satu mantra magis yang digunakan __file__. Sayangnya, jika Anda mengemas skrip Anda menggunakan cx_Freeze atau Anda menggunakan IDLE, ini akan menghasilkan pengecualian.

Mantra ajaib lainnya menggunakan os.getcwd (). Ini hanya akan berfungsi jika Anda menjalankan skrip dari command prompt dan direktori yang berisi skrip Anda adalah direktori kerja saat ini (yaitu Anda menggunakan perintah cd untuk mengubah ke direktori sebelum menjalankan skrip). Eh dewa! Saya harap saya tidak perlu menjelaskan mengapa ini tidak akan berhasil jika skrip Python Anda ada di PATH di suatu tempat dan Anda menjalankannya hanya dengan mengetik nama file skrip Anda.

Untungnya, ada mantra magis yang akan bekerja di semua kasus yang telah saya uji. Sayangnya, mantra magis lebih dari sekedar satu atau dua baris kode.

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)

Seperti yang Anda lihat, ini bukanlah tugas yang mudah!

Ben Key
sumber
1

Yang ini paling cocok untuk saya. Menggunakan:

os.path.abspath('')

Di mac itu harus mencetak sesuatu seperti:

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

Untuk mendapatkan jalur abs ke wd saat ini, yang ini lebih baik karena sekarang Anda bisa naik jika Anda mau, seperti ini:

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

Dan sekarang:

 '/Users/<your username>/'

Jadi jika Anda ingin mengimpor utilsdari sini, '/Users/<your username>/'
yang harus Anda lakukan adalah:

import sys
sys.path.append(os.path.abspath('../'))
Kohn1001
sumber
0

Saya melihat shebang dalam contoh Anda. Jika Anda menjalankan skrip bin Anda sebagai ./bin/foo.py, daripada python ./bin/foo.py, ada opsi menggunakan shebang untuk mengubah $PYTHONPATHvariabel.

Anda tidak dapat mengubah variabel lingkungan secara langsung di shebang, jadi Anda memerlukan skrip pembantu kecil. Taruh ini python.shke dalam binfolder Anda :

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

Dan kemudian ubah shebang Anda ./bin/foo.pymenjadi#!bin/python.sh

imbolc.dll
sumber
0

Saat kami mencoba menjalankan file python dengan jalur dari terminal.

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)

Simpan file (test.py).

Menjalankan sistem.

Buka terminal dan pergi ke direktori tempat menyimpan file. lalu menulis

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

Tekan enter

Keluaran:

File Name: test, Argument dir: /home/saiful/Desktop/bird.jpg
siful islam
sumber
0

Saya menggunakan:

from site import addsitedir

Kemudian, dapat menggunakan direktori relatif! addsitedir('..\lib') ; dua titik berarti memindahkan (ke atas) satu direktori terlebih dahulu.

Ingatlah bahwa itu semua tergantung pada direktori kerja Anda saat ini. Jika C: \ Joe \ Jen \ Becky, kemudian menambahkanitedir ('.. \ lib') impor ke jalur Anda C: \ Joe \ Jen \ lib

C:\
  |__Joe
      |_ Jen
      |     |_ Becky
      |_ lib
pengguna7693644
sumber
Saya menghargai tanggapannya, tetapi ini tidak menambahkan jalur relatif ke skrip yang sedang berjalan. Ia menambahkan jalur relatif ke direktori kerja saat ini
James Harr