Python: найкращий спосіб додати до sys.path щодо поточного запущеного сценарію


99

У мене є каталог, повний сценаріїв (скажімо project/bin). У мене також є бібліотека, project/libі я хочу, щоб сценарії завантажували її автоматично. Це те, що я зазвичай використовую вгорі кожного сценарію:

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

Це якось громіздко, негарно, і його потрібно вставити на початок кожного файлу. Чи є кращий спосіб це зробити?

Дійсно, на що я сподіваюся, це щось таке плавне, як це:

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

import mylib

Або ще краще, щось, що не зламається, коли мій редактор (або хтось інший, хто має доступ до комітів) вирішить змінити порядок імпорту в рамках процесу очищення:

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

Це не буде портувати безпосередньо на не-posix платформи, але це буде тримати речі в чистоті.


1
Дивіться також: stackoverflow.com/questions/2349991 / ...
dreftymac

Відповіді:


26

Якщо ви не хочете редагувати кожен файл

  • Встановіть вашу бібліотеку як звичайну бібліотеку python
    або
  • Встановіть PYTHONPATHна вашlib

або якщо ви бажаєте додати один рядок до кожного файлу, додайте оператор імпорту вгорі, наприклад

import import_my_lib

зберігайте import_my_lib.pyв кошику та import_my_libможете правильно встановити шлях python до того, що libви хочете


118

Це те, що я використовую:

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

2
Я б зробив sys.path.insert (0, ..), щоб його не перевизначили інші шляхи.
Джон Цзян,

Невже немає можливості змусити це запуститися автоматично?
Денис де Бернарді

1
Це трохи ризиковано. Якщо __file__це відносне ім'я файлу відносно поточного робочого каталогу (наприклад, setup.py), тоді os.path.dirname(__file__)буде порожнім рядком. Для цього і подібних проблем , піднятий Джоном Jiang , ekhumoro «S більш універсальний розчин сильно краще.
Сесіл Каррі,

29

Я використовую:

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

5
Я часто просто роблюsys.path.append('.')
Кімбо,

1
а якщо сценарій запускається з іншого каталогу? наприклад з кореневого каталогу, вказавши повний системний шлях? тоді os.getcwd () поверне "/"
обійхан

2
Ніколи цього не роби. Поточний робочий каталог (CWD) не гарантовано буде таким, яким ви вважаєте його - особливо в непередбачуваних крайових випадках, які ви точно мали бачити за милю. Просто посилайтеся __file__замість цього на будь-якого квазі-здорового розробника.
Сесіл Каррі,

14

Створіть модуль обгортки project/bin/lib, який містить це:

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

Тоді ви можете замінити всю суть у верхній частині ваших сценаріїв на:

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

9

Якщо ви не хочете будь-яким чином змінювати вміст сценарію, додайте поточний робочий каталог .до $ PYTHONPATH (див. Приклад нижче)

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

І назвіть це днем!


docs.python.org/3/tutorial/modules.html#the-module-search-path говорить: "sys.path ініціалізується з цих місць: -Довідник, що містить вхідний скрипт". Я думаю, що це неправда.

Я схильний робити це, особливо в .envrc, так що з direnv, який навіть є автоматичним і ізольованим.
EdvardM

8

Використання python 3.4+
Заборона використання cx_freeze або використання в IDLE. 😃

import sys
from pathlib import Path

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

Сумісний з Python2: import pathlib import os sys.path.append (os.path.dirname ( файл ))
Майкл

5

Ви можете запустити сценарій з python -mвідповідного кореневого каталогу. І передайте "шлях до модулів" як аргумент.

Приклад: $ python -m module.sub_module.main # Notice there is no '.py' at the end.


Інший приклад:

$ 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
Використання перемикача m - рекомендований спосіб зробити це, а не
злом

4

Існує проблема з кожною наданою відповіддю, яку можна резюмувати як "просто додайте це магічне заклинання на початок вашого сценарію. Подивіться, що ви можете зробити лише з одним рядком або двома кодами". Вони працюватимуть не у всіх можливих ситуаціях!

Наприклад, один з таких магічних заклинань використовує файл . На жаль, якщо ви пакуєте сценарій за допомогою cx_Freeze або використовуєте IDLE, це призведе до винятку.

Інший такий магічний заклинання використовує os.getcwd (). Це буде працювати лише в тому випадку, якщо ваш сценарій запущений із командного рядка, а каталог, що містить ваш сценарій, є поточним робочим каталогом (тобто ви використовували команду cd для переходу в каталог до запуску сценарію). Е боги! Сподіваюся, мені не доведеться пояснювати, чому це не спрацює, якщо ваш скрипт Python десь знаходиться в PATH, і ви запустили його, просто ввівши ім'я вашого файлу сценарію.

На щастя, існує магічне заклинання, яке спрацює у всіх випробуваних мною випадках. На жаль, магічне заклинання - це більше, ніж просто рядок-два коду.

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)

Як бачите, це непросте завдання!


0

Цей найкраще працює для мене. Використання:

os.path.abspath('')

На mac він повинен надрукувати щось на зразок:

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

Щоб отримати шлях абс до поточного wd, це краще, тому що тепер ви можете піднятися, якщо хочете, наприклад:

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

А зараз:

 '/Users/<your username>/'

Отже, якщо ви хочете імпортувати utilsзвідси, '/Users/<your username>/'
вам залишається лише:

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

0

Я бачу на вашому прикладі шебанга. Якщо ви запускаєте свої скрипти bin як ./bin/foo.py, а не python ./bin/foo.py, існує можливість використання shebang для зміни $PYTHONPATHзмінної.

Однак ви не можете змінювати змінні середовища безпосередньо в shebangs, тому вам знадобиться невеликий допоміжний скрипт. Помістіть це python.shу свою binпапку:

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

А потім змінити шебанга свого ./bin/foo.pyна#!bin/python.sh


0

Коли ми намагаємося запустити файл python із шляхом із терміналу.

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)

Збережіть файл (test.py).

Ходова система.

Відкрийте термінал і перейдіть до тієї директорії, де зберігається файл. тоді пиши

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

Натисніть Enter

Вихід:

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

0

Я використовую:

from site import addsitedir

Тоді можна використовувати будь-який відносний каталог! addsitedir('..\lib') ; дві крапки означають переміщення (вгору) на один каталог спочатку.

Пам'ятайте, що все залежить від того, з якого поточного робочого каталогу ви починаєте. Якщо C: \ Joe \ Jen \ Becky, тоді additedir ('.. \ lib') імпортує до вашого шляху C: \ Joe \ Jen \ lib

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

Я вдячний за відповідь, але це не додає шлях щодо поточного запущеного сценарію. Він додає шлях відносно поточного робочого каталогу
James Harr
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.