Expanda o caminho de pesquisa do Python para outra fonte

106

Acabei de entrar em um projeto com uma base de código existente bastante grande. Desenvolvemos em linux e não utilizamos IDE. Corremos pela linha de comando. Estou tentando descobrir como fazer com que o python procure o caminho certo quando executo módulos de projeto. Por exemplo, quando executo algo como:

python someprojectfile.py

eu recebo

ImportError: no module named core.'somemodule'

Recebo isso para todas as minhas importações e presumo que seja um problema com o caminho.

TLDR:

Como faço para que o Python pesquise ~/codez/project/e todos os arquivos e pastas para arquivos * .py durante as instruções de importação.

O maestro
fonte

Respostas:

171

Existem algumas maneiras possíveis de fazer isso:

  • Defina a variável de ambiente PYTHONPATHcomo uma lista de diretórios separada por dois pontos para pesquisar módulos importados.
  • Em seu programa, use sys.path.append('/path/to/search')para adicionar os nomes dos diretórios que deseja que o Python procure por módulos importados. sys.pathé apenas a lista de diretórios que o Python pesquisa sempre que é solicitado a importar um módulo, e você pode alterá-la conforme necessário (embora eu não recomende remover nenhum dos diretórios padrão!). Todos os diretórios que você colocar na variável de ambiente PYTHONPATHserão inseridos sys.pathquando o Python for inicializado.
  • Use site.addsitedirpara adicionar um diretório sys.path. A diferença entre isso e o acréscimo simples é que, quando você usa addsitedir, ele também procura por .ptharquivos dentro desse diretório e os usa para possivelmente adicionar diretórios adicionais com sys.pathbase no conteúdo dos arquivos. Veja a documentação para mais detalhes.

Qual destes você deseja usar depende da sua situação. Lembre-se de que quando você distribui seu projeto para outros usuários, eles normalmente o instalam de forma que os arquivos de código Python sejam detectados automaticamente pelo importador de Python (ou seja, os pacotes são geralmente instalados no site-packagesdiretório), portanto, se você mexer com sys.pathseu código , isso pode ser desnecessário e pode até ter efeitos adversos quando o código é executado em outro computador. Para o desenvolvimento, eu arriscaria supor que a configuração PYTHONPATHgeralmente é o melhor caminho a seguir.

No entanto, quando você está usando algo que apenas roda em seu próprio computador (ou quando você tem configurações não padrão, por exemplo, às vezes em estruturas de aplicativos da web), não é totalmente incomum fazer algo como

import sys
from os.path import dirname
sys.path.append(dirname(__file__))
David Z
fonte
Então, se eu tivesse, digamos, 15 subdiretórios, eu teria que adicionar cada um individualmente?
themaestro
e você poderia dar um exemplo de um argumento de linha de comando para alterar PYTHONPATH?
themaestro
3
Para definir PYTHONPATH: em .bashrcou em qualquer arquivo de inicialização que seu shell usa (se não for Bash), escreva export PYTHONPATH=$PYTHONPATH:$HOME/codez/project. Mas se você tiver vários subdiretórios, eu faria um .ptharquivo e usaria site.addsitedir. Você pode criar um módulo sitecustomizeque pode chamar a função para você; tente colocá-lo em ~/.local/lib/python2.6/sitecustomize.py(substitua sua versão Python) para que seja importado automaticamente.
David Z
Coloquei o seguinte no meu arquivo .bashrc e ainda não estou tendo sorte com essas importações. Alguma ideia? Como eu faria um arquivo .pth mesmo assim? export PYTHONPATH = $ PYTHONPATH: $ HOME / adaifotis / codez / export PYTHONPATH = $ PYTHONPATH: $ HOME / adaifotis / codez / projeto export PYTHONPATH = $ PYTHONPATH: $ HOME / adaifotis / codez / project / core export PYTHONPATH = $ PATHONPATH HOME / adaifotis / codez / project / proxies export PYTHONPATH = $ PYTHONPATH: $ HOME / adaifotis / codez / project / conf
themaestro
Tente abrir um terminal e executar echo $PYTHONPATH. Se a variável de ambiente foi configurada corretamente, você deve ver uma lista de diretórios separados por dois pontos. Para obter informações sobre os .ptharquivos, consulte a documentação do sitemódulo ao qual vinculei em minha resposta. Ele informa qual deve ser o conteúdo e como usá-lo.
David Z
13

Você também deve ler sobre os pacotes python aqui: http://docs.python.org/tutorial/modules.html .

Pelo seu exemplo, acho que você realmente tem um pacote em ~/codez/project. O arquivo __init__.pyem um diretório python mapeia um diretório em um namespace. Se todos os seus subdiretórios tiverem um __init__.pyarquivo, você só precisará adicionar o diretório base ao seu PYTHONPATH. Por exemplo:

PYTHONPATH = $ PYTHONPATH: $ HOME / adaifotis / projeto

Além de testar sua variável de ambiente PYTHONPATH, como David explica, você pode testá-la em python desta forma:

$ python
>>> import project                      # should work if PYTHONPATH set
>>> import sys
>>> for line in sys.path: print line    # print current python path

...

Andrew B.
fonte
Você pode querer colocar seus __init__.pybackticks para deixar claro que você realmente quer dizer __init__.py, ao invés de init.py. Apenas para evitar confundir novatos. :)
antred
4

Eu sei que este tópico é um pouco antigo, mas levei algum tempo para chegar ao cerne dele, então eu queria compartilhar.

No meu projeto, eu tinha o script principal em um diretório pai e, para diferenciar os módulos, coloquei todos os módulos de suporte em uma subpasta chamada "módulos". No meu script principal, eu importo esses módulos como este (para um módulo chamado report.py):

from modules.report import report, reportError

Se eu chamar meu script principal, isso funciona. NO ENTANTO, eu queria testar cada módulo incluindo um main()em cada um e chamando cada um diretamente, como:

python modules/report.py

Agora o Python reclama que não consegue encontrar "um módulo chamado módulos". A chave aqui é que, por padrão, Python inclui a pasta do script em seu caminho de pesquisa, MAS NÃO O CWD. Então, o que esse erro diz, na verdade, é "Não consigo encontrar uma subpasta de módulos". Isso ocorre porque não há subdiretório "modules" no diretório onde o módulo report.py reside.

Acho que a solução mais legal para isso é anexar o CWD no caminho de pesquisa do Python incluindo isso no topo:

import sys

sys.path.append(".")

Agora o Python pesquisa o CWD (diretório atual), encontra a subpasta "modules" e está tudo bem.

Tom Gordon
fonte
3

Li esta pergunta procurando uma resposta, mas não gostei de nenhuma delas.

Então, escrevi uma solução rápida e suja. Basta colocá-lo em algum lugar do seu sys.path e ele adicionará qualquer diretório em folder(do diretório de trabalho atual) ou em abspath:

#using.py

import sys, os.path

def all_from(folder='', abspath=None):
    """add all dirs under `folder` to sys.path if any .py files are found.
    Use an abspath if you'd rather do it that way.

    Uses the current working directory as the location of using.py. 
    Keep in mind that os.walk goes *all the way* down the directory tree.
    With that, try not to use this on something too close to '/'

    """
    add = set(sys.path)
    if abspath is None:
        cwd = os.path.abspath(os.path.curdir)
        abspath = os.path.join(cwd, folder)
    for root, dirs, files in os.walk(abspath):
        for f in files:
            if f[-3:] in '.py':
                add.add(root)
                break
    for i in add: sys.path.append(i)

>>> import using, sys, pprint
>>> using.all_from('py') #if in ~, /home/user/py/
>>> pprint.pprint(sys.path)
[
#that was easy
]

E eu gosto porque posso ter uma pasta para algumas ferramentas aleatórias e não fazer com que façam parte de pacotes ou qualquer coisa, e ainda ter acesso a alguns (ou todos) em algumas linhas de código.

Droogans
fonte
3

A maneira mais fácil que encontrei é criar um arquivo "any_name.pth" e colocá-lo na pasta "\ Lib \ site-packages". Você deve encontrar essa pasta onde quer que o python esteja instalado.

Nesse arquivo, coloque uma lista de diretórios onde deseja manter os módulos para importação. Por exemplo, faça uma linha nesse arquivo como esta:

C: \ Usuários \ exemplo ... \ exemplo

Você saberá que funciona executando o seguinte em python:

import sys
for line in sys: print line

Você verá seu diretório impresso, entre outros de onde também pode importar. Agora você pode importar um arquivo "mymodule.py" que fica nesse diretório tão facilmente quanto:

import mymodule

Isso não importará subpastas. Para isso, você pode imaginar a criação de um script python para criar um arquivo .pth contendo todas as subpastas de uma pasta que você definir. Faça-o rodar na inicialização, talvez.

KieranPC
fonte
0

Nova opção para pergunta antiga.
Instalando o fail2banpacote no Debian, parece que ele está codificado para instalar em /usr/lib/python3/dist-packages/fail2banum caminho diferente do python3 sys.path.


> python3
Python 3.7.3 (v3.7.3:ef4ec6ed12, Jun 25 2019, 18:51:50)
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
['', '/usr/lib/python37.zip', '/usr/lib/python3.7', '/usr/lib/python3.7/lib-dynload', '/usr/lib/python3.7/site-packages']
>>>

então, em vez de apenas copiar, eu (bash) vinculei a biblioteca a versões mais recentes.
Futuras atualizações do aplicativo original também serão aplicadas automaticamente às versões vinculadas.

 if [ -d /usr/lib/python3/dist-packages/fail2ban ]
   then
      for d in /usr/lib/python3.*
      do
         [ -d ${d}/fail2ban ] || \
            ln -vs /usr/lib/python3/dist-packages/fail2ban ${d}/
      done
   fi
fcm
fonte