Python: melhor maneira de adicionar ao sys.path em relação ao script em execução no momento

98

Tenho um diretório cheio de scripts (digamos project/bin). Também tenho uma biblioteca localizada em project/libe desejo que os scripts a carreguem automaticamente. Isso é o que eu normalmente uso no início de cada 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

Isso é meio complicado, feio e deve ser colado no início de cada arquivo. Existe uma maneira melhor de fazer isso?

Realmente, o que espero é algo tão suave quanto isto:

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

import mylib

Ou, melhor ainda, algo que não quebraria quando meu editor (ou outra pessoa que tenha acesso de commit) decida reordenar as importações como parte de seu processo de limpeza:

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

Isso não portaria diretamente para plataformas não-posix, mas manteria as coisas limpas.

James Harr
fonte
1
Veja também: stackoverflow.com/questions/2349991/…
dreftymac

Respostas:

26

Se você não quiser editar cada arquivo

  • Instale sua biblioteca como uma biblioteca python normal
    ou
  • Defina PYTHONPATHpara o seulib

ou se você deseja adicionar uma única linha a cada arquivo, adicione uma declaração de importação no topo, por exemplo

import import_my_lib

manter import_my_lib.pyno lixo e import_my_libpode definir corretamente o caminho python para o que libvocê quiser

Anurag Uniyal
fonte
118

Isso é o que eu uso:

import os, sys
sys.path.append(os.path.join(os.path.dirname(__file__), "lib"))
Jterrace
fonte
2
Eu faria sys.path.insert (0, ..) para que ele não fosse substituído por outros caminhos.
John Jiang
Não há realmente nenhuma maneira de fazer isso funcionar automaticamente?
Denis de Bernardy
1
Isso é um pouco arriscado. Se __file__for um nome de arquivo relativo ao diretório de trabalho atual (por exemplo, setup.py), os.path.dirname(__file__)será a string vazia. Por esta e preocupações semelhantes levantada por John Jiang , ekhumoro é mais solução de propósito geral é fortemente preferível.
Cecil Curry
29

Estou a usar:

import sys,os
sys.path.append(os.getcwd())
DusX
fonte
5
Costumo apenas fazersys.path.append('.')
kimbo
1
e se o script estiver sendo executado em um diretório diferente? por exemplo, do diretório raiz, fornecendo o caminho completo do sistema? então os.getcwd () retornará "/"
obayhan
2
Nunca faça isso. Não é garantido que o diretório de trabalho atual (CWD) seja o que você pensa que é - especialmente em casos extremos imprevisíveis que você definitivamente deveria ter visto chegando a um quilômetro de distância. Em __file__vez disso, basta fazer referência como qualquer desenvolvedor quase são.
Cecil Curry
14

Crie um módulo de wrapper project/bin/lib, que contém este:

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

Em seguida, você pode substituir todo o conteúdo cruft na parte superior de seus scripts por:

#!/usr/bin/python
from lib import mylib
Ekhumoro
fonte
8

Se você não quiser alterar o conteúdo do script de nenhuma forma, inclua o prefixo do diretório de trabalho atual .em $ PYTHONPATH (veja o exemplo abaixo)

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

E chamá-lo um dia!

Khanh Hua
fonte
docs.python.org/3/tutorial/modules.html#the-module-search-path diz: "sys.path é inicializado a partir destes locais: -O diretório que contém o script de entrada". Eu acho que isso não é verdade.
Eu costumo fazer isso, especialmente em .envrc, então com direnv isso é automático e isolado também.
EdvardM de
8

Usando python 3.4+ Barrando
o uso de cx_freeze ou em IDLE. 😃

import sys
from pathlib import Path

sys.path.append(Path(__file__).parent / "lib")
GollyJer
fonte
Compatível com Python2: import pathlib import os sys.path.append (os.path.dirname ( file ))
michael
5

Você pode executar o script a python -mpartir do diretório raiz relevante. E passe o "caminho dos módulos" como argumento.

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


Outro exemplo:

$ 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
fonte
1
Usar a opção m é a maneira recomendada de fazer isso e não os hacks de caminho do sys
Mr_and_Mrs_D
4

Há um problema com cada resposta fornecida que pode ser resumido como "apenas adicione este encantamento mágico ao início do seu script. Veja o que você pode fazer com apenas uma linha ou duas de código." Eles não funcionarão em todas as situações possíveis!

Por exemplo, um desses encantamentos mágicos usa arquivo . Infelizmente, se você empacotar seu script usando cx_Freeze ou estiver usando IDLE, isso resultará em uma exceção.

Outro encantamento mágico usa os.getcwd (). Isso só funcionará se você estiver executando o script a partir do prompt de comando e o diretório que contém o script for o diretório de trabalho atual (ou seja, você usou o comando cd para mudar para o diretório antes de executar o script). Eh deuses! Espero não ter que explicar por que isso não funcionará se o seu script Python está no PATH em algum lugar e você o executou simplesmente digitando o nome do seu arquivo de script.

Felizmente, existe um encantamento mágico que funcionará em todos os casos que testei. Infelizmente, o encantamento mágico é mais do que apenas uma ou duas linhas de código.

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)

Como você pode ver, não é uma tarefa fácil!

Ben Key
fonte
0

Este funciona melhor para mim. Usar:

os.path.abspath('')

No mac, deve imprimir algo como:

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

Para obter o caminho do abs para o wd atual, este é melhor porque agora você pode subir se quiser, assim:

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

E agora:

 '/Users/<your username>/'

Então, se você quiser importar utilsdaqui, '/Users/<your username>/'
tudo o que você precisa fazer é:

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

Eu vejo um shebang em seu exemplo. Se você estiver executando seus scripts bin ./bin/foo.py, em vez de python ./bin/foo.py, há uma opção de usar o shebang para alterar a $PYTHONPATHvariável.

No entanto, você não pode alterar as variáveis ​​de ambiente diretamente em shebangs, então você precisará de um pequeno script auxiliar. Coloque python.shem sua binpasta:

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

E então mude a essência do seu ./bin/foo.pypara ser#!bin/python.sh

imbolc
fonte
0

Quando tentamos executar o arquivo python com o caminho do 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)

Salve o arquivo (test.py).

Sistema de execução.

Abra o terminal e vá para aquele diretório onde está o arquivo de salvamento. então escreva

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

Pressione Enter

Resultado:

File Name: test, Argument dir: /home/saiful/Desktop/bird.jpg
Siful Islão
fonte