Como recuperar o caminho de um módulo?

757

Quero detectar se o módulo mudou. Agora, usando o inotify é simples, você só precisa saber o diretório em que deseja receber notificações.

Como recuperar o caminho de um módulo em python?

Alegre
fonte
9
Se você ainda está procurando neste site, atualize a resposta correta para isso . É muito mais limpo que a solução proposta e também funciona nos casos em que __file__não está definido.
Erikbwork
3
@ erikb85: não é apenas mais limpo; inspectA solução baseada em dados também funciona para execfile()maiúsculas e minúsculas quando __file__produz um nome errado silenciosamente.
jfs
import pathlib, module; pathlib.Path(module.__file__).resolve().parentIsso é
dependente de

Respostas:

907
import a_module
print(a_module.__file__)

Na verdade, você fornecerá o caminho para o arquivo .pyc que foi carregado, pelo menos no Mac OS X. Então, acho que você pode:

import os
path = os.path.abspath(a_module.__file__)

Você também pode tentar:

path = os.path.dirname(a_module.__file__)

Para obter o diretório do módulo.

orestis
fonte
54
isso responde como obter o caminho do módulo que você importa, mas não o módulo / script em que você está (para o script que você está executando, __file__não é um caminho completo, é relativo). Para o arquivo em que estou, tive que importar outro módulo do mesmo diretório e fazer como mostrado aqui. Alguém conhece uma maneira mais conveniente?
Ben Bryant
5
@hbdgaf certeza que não existe tal coisa como um built-inself.__file__
Dan Passaro
26
@BenBryant @hbdgaf os.path.dirname(__file__)funciona bem para mim e retorna o caminho abs do diretório do módulo.
Niccolò
9
Eu tentei fazer isso e recebi o traceback: AttributeError: 'module' object has no attribute '__file__'
Dorian Dore
5
@DorianDore Eu estava mexendo um pouco com os módulos e cheguei a uma solução path = module.__path__.__dict__["_path"][0], mas não tenho certeza se é portátil ou se não difere entre as versões do python. Ele está trabalhando para mim, ao contrário esta resposta que me dá o mesmo erro e inspectaumentos resposta TypeError: <module 'module' (namespace)> is a built-in module...
Jezor
279

inspectmódulo em python.

Documentação oficial

O módulo inspecionar fornece várias funções úteis para ajudar a obter informações sobre objetos ativos, como módulos, classes, métodos, funções, rastreamentos, objetos de quadro e objetos de código. Por exemplo, ele pode ajudá-lo a examinar o conteúdo de uma classe, recuperar o código fonte de um método, extrair e formatar a lista de argumentos de uma função ou obter todas as informações necessárias para exibir um rastreamento detalhado.

Exemplo:

>>> import os
>>> import inspect
>>> inspect.getfile(os)
'/usr/lib64/python2.7/os.pyc'
>>> inspect.getfile(inspect)
'/usr/lib64/python2.7/inspect.pyc'
>>> os.path.dirname(inspect.getfile(inspect))
'/usr/lib64/python2.7'
Tomas Tomecek
fonte
7
Você também pode usar inspectpara obter o nome do arquivo atual; veja stackoverflow.com/a/50905/320036
z0r
5
Pesquisei essa pergunta várias vezes no Google e esta é a resposta mais razoável que já vi! Atualize as informações sobreinspect.currentframe()
erikbwork
a inspect.getfile()abordagem não funciona com o módulo _io, mas funciona com o módulo io.
Smdikipedia
70

Como as outras respostas disseram, a melhor maneira de fazer isso é com __file__(demonstrado novamente abaixo). No entanto, existe uma ressalva importante, que __file__NÃO existe se você estiver executando o módulo por conta própria (por exemplo, como __main__).

Por exemplo, digamos que você tenha dois arquivos (ambos no PYTHONPATH):

#/path1/foo.py
import bar
print(bar.__file__)

e

#/path2/bar.py
import os
print(os.getcwd())
print(__file__)

Executar foo.py fornecerá a saída:

/path1        # "import bar" causes the line "print(os.getcwd())" to run
/path2/bar.py # then "print(__file__)" runs
/path2/bar.py # then the import statement finishes and "print(bar.__file__)" runs

No entanto, se você tentar executar o bar.py por conta própria, obterá:

/path2                              # "print(os.getcwd())" still works fine
Traceback (most recent call last):  # but __file__ doesn't exist if bar.py is running as main
  File "/path2/bar.py", line 3, in <module>
    print(__file__)
NameError: name '__file__' is not defined 

Espero que isto ajude. Essa advertência me custou muito tempo e confusão ao testar as outras soluções apresentadas.

Mcstrother
fonte
5
Nesse caso, você pode usar sys.argv [0] no lugar do arquivo .
21713 Jimothy
4
Isso é uma advertência específica da versão? Nas versões 2.6 e 2.7, eu confio no arquivo , que funciona quando o nome __ == '__ main '. O único caso de falha que eu vi foi com "python -c 'print file '". Acrescentarei que algumas vezes o arquivo pode ser '<stdin>', o que acontece quando IDEs como o emacs executam o buffer atual.
Paul Du Bois
1
Note-se que levando-e-fuga "__" caracteres colocar uma palavra em negrito, de modo manter isso em mente ao ler os comentários anteriores :-P
Paul Du Bois
1
@PaulDuBois Você pode cercá-lo com tiques traseiros: ` __file__` torna__file__
fncomp
1
Como você consegue o NameError? @ Paul Du Bois: Eu tentei Python 2,3-3,4 e __file__é definida no entanto eu executar o arquivo Python: python a.py, python -ma, ./a.py.
JFS
43

Vou tentar abordar algumas variações dessa questão também:

  1. localizando o caminho do script chamado
  2. localizando o caminho do script atualmente em execução
  3. localizando o diretório do script chamado

(Algumas dessas perguntas foram feitas no SO, mas foram fechadas como duplicatas e redirecionadas aqui.)

Advertências de uso __file__

Para um módulo que você importou:

import something
something.__file__ 

retornará o caminho absoluto do módulo. No entanto, dado o seguinte script foo.py:

#foo.py
print '__file__', __file__

Chamando-o com 'python foo.py' Retornará simplesmente 'foo.py'. Se você adicionar um shebang:

#!/usr/bin/python 
#foo.py
print '__file__', __file__

e chame-o usando ./foo.py, ele retornará './foo.py'. Chamando-o de um diretório diferente (por exemplo, coloque foo.py na barra de diretórios) e, em seguida, chamando

python bar/foo.py

ou adicionando um shebang e executando o arquivo diretamente:

bar/foo.py

retornará 'bar / foo.py' (o parente caminho ).

Localizando o diretório

Agora, a partir daí, para obter o diretório, os.path.dirname(__file__)também pode ser complicado. Pelo menos no meu sistema, ele retornará uma string vazia se você a chamar do mesmo diretório que o arquivo. ex.

# foo.py
import os
print '__file__ is:', __file__
print 'os.path.dirname(__file__) is:', os.path.dirname(__file__)

irá produzir:

__file__ is: foo.py
os.path.dirname(__file__) is: 

Em outras palavras, ele retorna uma cadeia vazia, portanto, isso não parece confiável se você deseja usá-la para o arquivo atual (ao contrário do arquivo de um módulo importado). Para contornar isso, você pode envolvê-lo em uma chamada para abspath:

# foo.py
import os
print 'os.path.abspath(__file__) is:', os.path.abspath(__file__)
print 'os.path.dirname(os.path.abspath(__file__)) is:', os.path.dirname(os.path.abspath(__file__))

que gera algo como:

os.path.abspath(__file__) is: /home/user/bar/foo.py
os.path.dirname(os.path.abspath(__file__)) is: /home/user/bar

Observe que abspath () NÃO resolve links simbólicos. Se você quiser fazer isso, use realpath (). Por exemplo, criando um link simbólico file_import_testing_link apontando para file_import_testing.py, com o seguinte conteúdo:

import os
print 'abspath(__file__)',os.path.abspath(__file__)
print 'realpath(__file__)',os.path.realpath(__file__)

A execução imprimirá caminhos absolutos, como:

abspath(__file__) /home/user/file_test_link
realpath(__file__) /home/user/file_test.py

file_import_testing_link -> file_import_testing.py

Usando inspecionar

O @SummerBreeze menciona o uso do módulo inspecionar .

Isso parece funcionar bem e é bastante conciso para os módulos importados:

import os
import inspect
print 'inspect.getfile(os) is:', inspect.getfile(os)

obedientemente retorna o caminho absoluto. No entanto, para encontrar o caminho do script em execução no momento, não vi uma maneira de usá-lo.

jpgeek
fonte
1
inspect.getfile (os) é o mesmo que os .__ file__ do código: def getfile (object): "" "Calcule em qual arquivo de origem ou compilado um objeto foi definido." "" if ismodule (object): if hasattr (objeto, ' arquivo '): retornar objeto .__ arquivo___
idanzalz
4
Você pode usar inspect.getfile(inspect.currentframe())para obter o caminho do script em execução no momento.
jbochi
33

Não entendo por que ninguém está falando sobre isso, mas para mim a solução mais simples é usar imp.find_module ("modulename") (documentação aqui ):

import imp
imp.find_module("os")

Ele fornece uma tupla com o caminho na segunda posição:

(<open file '/usr/lib/python2.7/os.py', mode 'U' at 0x7f44528d7540>,
'/usr/lib/python2.7/os.py',
('.py', 'U', 1))

A vantagem desse método sobre o "inspecionar" é que você não precisa importar o módulo para fazê-lo funcionar e pode usar uma string na entrada. Útil ao verificar os módulos chamados em outro script, por exemplo.

EDIT :

No python3, o importlibmódulo deve fazer:

Doc de importlib.util.find_spec:

Retorne a especificação para o módulo especificado.

Primeiro, o sys.modules é verificado para ver se o módulo já foi importado. Se sim, então sys.modules [nome]. especificação é retornada. Se isso for definido como Nenhum, ValueError será gerado. Se o módulo não estiver no sys.modules, o sys.meta_path será procurado por uma especificação adequada com o valor de 'path' fornecido aos localizadores. Nenhum será retornado se nenhuma especificação puder ser encontrada.

Se o nome for para o submódulo (contém um ponto), o módulo pai é importado automaticamente.

Os argumentos de nome e pacote funcionam da mesma maneira que importlib.import_module (). Em outras palavras, nomes de módulos relativos (com pontos à esquerda) funcionam.

PlasmaBinturong
fonte
2
imp NÃO é depreciado no python 2 (versão atual 2.7.13). imp é depreciado no python 3 desde 3.4. importlib deve ser usado no python 3. Eu gosto desta solução, porque ele funciona até mesmo quando a importação real irá falhar (por exemplo, porque módulo de 64 bits para um motor de 32 bits)
mdew
E REALMENTE bom ao tentar encontrar o caminho do sqlite3 de 64 bits e a importação falhar. Perfeito.
rahvin_t
2
importlib.machinery.PathFinder().find_module("os").get_filename()Alternativa mais imp.find_modulecurta que eu encontrei no Python3 +. Se alguém está procurando o uso de importlib.util.find_spec.
Torxed 24/03/19
Esse método funciona sem importar o módulo real, o que é ótimo, pois estou usando isso para descobrir qual versão de um módulo está sendo importada de um computador compartilhado.
Gouda
19

Isso foi trivial.

Cada módulo possui uma __file__variável que mostra seu caminho relativo de onde você está agora.

Portanto, obter um diretório para o módulo notificá-lo é simples como:

os.path.dirname(__file__)
Alegre
fonte
14
Quase, mas não completamente, o arquivo não é "relativo a onde você está agora"; quando é relativo (que será apenas quando houver caminhos relativos no sys.path), é relativo ao local onde você estava quando o módulo foi carregado .
Charles Duffy
17
import os
path = os.path.abspath(__file__)
dir_path = os.path.dirname(path)
vinoth
fonte
1
não funciona no meu Linux python 2.6, uma vez que __file__é apenas dir / test.py, o abspath envolve o cwd para concluir o nome do caminho que não é o resultado desejado, mas se você importar um módulo, m.__file__obtém o resultado desejado.
Ben Bryant
10
import module
print module.__path__

Pacotes suportam mais um atributo especial __path__,. Isso é inicializado para ser uma lista que contém o nome do diretório que contém o pacote __init__.pyantes que o código nesse arquivo seja executado. Esta variável pode ser modificada; isso afeta futuras pesquisas por módulos e subpacotes contidos no pacote.

Embora esse recurso não seja frequentemente necessário, ele pode ser usado para estender o conjunto de módulos encontrados em um pacote.

Fonte

Lukas Greblikas
fonte
9

Utilitário de Linha de Comando

Você pode ajustá-lo para um utilitário de linha de comando,

python-which <package name>

insira a descrição da imagem aqui


Crio /usr/local/bin/python-which

#!/usr/bin/env python

import importlib
import os
import sys

args = sys.argv[1:]
if len(args) > 0:
    module = importlib.import_module(args[0])
    print os.path.dirname(module.__file__)

Torne executável

sudo chmod +x /usr/local/bin/python-which
Jossef Harush
fonte
7

Por isso, passei bastante tempo tentando fazer isso com o py2exe. O problema era obter a pasta base do script, se ele estava sendo executado como um script python ou como um executável py2exe. Também para que funcione se estava sendo executado a partir da pasta atual, de outra pasta ou (essa foi a mais difícil) do caminho do sistema.

Eventualmente, usei essa abordagem, usando sys.frozen como um indicador de execução no py2exe:

import os,sys
if hasattr(sys,'frozen'): # only when running in py2exe this exists
    base = sys.prefix
else: # otherwise this is a regular python script
    base = os.path.dirname(os.path.realpath(__file__))
uri
fonte
7

você pode simplesmente importar seu módulo, clicar no nome e obter o caminho completo

>>> import os
>>> os
<module 'os' from 'C:\\Users\\Hassan Ashraf\\AppData\\Local\\Programs\\Python\\Python36-32\\lib\\os.py'>
>>>
Hassan Ashraf
fonte
4

Se você deseja recuperar o caminho raiz do pacote de qualquer um de seus módulos, o seguinte funciona (testado no Python 3.6):

from . import __path__ as ROOT_PATH
print(ROOT_PATH)

O __init__.pycaminho principal também pode ser referenciado usando em __file__vez disso.

Espero que isto ajude!

fr_andres
fonte
3

Se a única ressalva de uso __file__for atual, o diretório relativo estiver em branco (ou seja, ao executar como um script a partir do mesmo diretório em que o script está), uma solução trivial será:

import os.path
mydir = os.path.dirname(__file__) or '.'
full  = os.path.abspath(mydir)
print __file__, mydir, full

E o resultado:

$ python teste.py 
teste.py . /home/user/work/teste

O truque está or '.'após a dirname()ligação. Ele define o diretório como ., o que significa diretório atual e é um diretório válido para qualquer função relacionada ao caminho.

Assim, o uso abspath()não é realmente necessário. Mas se você o usar de qualquer maneira, o truque não será necessário: abspath()aceita caminhos em branco e o interpreta adequadamente como o diretório atual.

MestreLion
fonte
É melhor trocar a ordem e usar apenas os.path.dirname(os.path.abspath(__file__))porque, assim, você não precisa se preocupar com caminhos relativos. Uma oportunidade a menos para erros.
szali
3

Eu gostaria de contribuir com um cenário comum (no Python 3) e explorar algumas abordagens para ele.

A função interna open () aceita o caminho relativo ou absoluto como seu primeiro argumento. O caminho relativo é tratado como relativo ao diretório de trabalho atual, portanto, é recomendável passar o caminho absoluto para o arquivo.

Basta dizer, se você executar um arquivo de script com o seguinte código, é não garantiu que o example.txtarquivo será criado no mesmo diretório onde o arquivo de script está localizado:

with open('example.txt', 'w'):
    pass

Para corrigir esse código, precisamos obter o caminho para o script e torná-lo absoluto. Para garantir que o caminho seja absoluto, simplesmente usamos a função os.path.realpath () . Para obter o caminho para o script, existem várias funções comuns que retornam vários resultados do caminho:

  • os.getcwd()
  • os.path.realpath('example.txt')
  • sys.argv[0]
  • __file__

Ambas as funções os.getcwd () e os.path.realpath () retornam resultados do caminho com base no diretório de trabalho atual . Geralmente não é o que queremos. O primeiro elemento da lista sys.argv é o caminho do script raiz (o script que você executa), independentemente de você chamar a lista no próprio script raiz ou em qualquer um de seus módulos. Pode ser útil em algumas situações. A variável __file__ contém o caminho do módulo do qual foi chamado.


O código a seguir cria corretamente um arquivo example.txtno mesmo diretório em que o script está localizado:

filedir = os.path.dirname(os.path.realpath(__file__))
filepath = os.path.join(filedir, 'example.txt')

with open(filepath, 'w'):
    pass
Jeyekomon
fonte
3

Se você deseja conhecer o caminho absoluto do seu script, pode usar o objeto Path :

from pathlib import Path

print(Path().absolute())
print(Path().resolve('.'))
print(Path().cwd())

método cwd ()

Retorne um novo objeto de caminho que representa o diretório atual (retornado por os.getcwd ())

método resolve ()

Torne o caminho absoluto, resolvendo quaisquer links simbólicos. Um novo objeto de caminho é retornado:

Vlad Bezden
fonte
1

Nos módulos de um pacote python, tive que me referir a um arquivo que residia no mesmo diretório que o pacote. Ex.

some_dir/
  maincli.py
  top_package/
    __init__.py
    level_one_a/
      __init__.py
      my_lib_a.py
      level_two/
        __init__.py
        hello_world.py
    level_one_b/
      __init__.py
      my_lib_b.py

Então, acima, eu tive que chamar maincli.py do módulo my_lib_a.py, sabendo que top_package e maincli.py estão no mesmo diretório. Aqui está como eu obtenho o caminho para maincli.py:

import sys
import os
import imp


class ConfigurationException(Exception):
    pass


# inside of my_lib_a.py
def get_maincli_path():
    maincli_path = os.path.abspath(imp.find_module('maincli')[1])
    # top_package = __package__.split('.')[0]
    # mod = sys.modules.get(top_package)
    # modfile = mod.__file__
    # pkg_in_dir = os.path.dirname(os.path.dirname(os.path.abspath(modfile)))
    # maincli_path = os.path.join(pkg_in_dir, 'maincli.py')

    if not os.path.exists(maincli_path):
        err_msg = 'This script expects that "maincli.py" be installed to the '\
        'same directory: "{0}"'.format(maincli_path)
        raise ConfigurationException(err_msg)

    return maincli_path

Com base na postagem de PlasmaBinturong, modifiquei o código.

Al Conrad
fonte
1

Se você deseja fazer isso dinamicamente em um "programa", tente este código:
Meu argumento é que você talvez não saiba o nome exato do módulo para "codificá-lo". Pode ser selecionado em uma lista ou não estar em execução no momento para usar __file__.

(Eu sei, não vai funcionar no Python 3)

global modpath
modname = 'os' #This can be any module name on the fly
#Create a file called "modname.py"
f=open("modname.py","w")
f.write("import "+modname+"\n")
f.write("modpath = "+modname+"\n")
f.close()
#Call the file with execfile()
execfile('modname.py')
print modpath
<module 'os' from 'C:\Python27\lib\os.pyc'>

Tentei me livrar do problema "global", mas encontrei casos em que não funcionava. Acho que "execfile ()" pode ser emulado no Python 3 Como esse é um programa, ele pode ser facilmente colocado em um método ou módulo para reuso.

Robin Randall
fonte
0

Se você o instalou usando o pip, "pip show" funciona muito bem ('Localização')

$ pip show detectron2

Name: detectron2
Version: 0.1
Summary: Detectron2 is FAIR next-generation research platform for object detection and segmentation.
Home-page: https://github.com/facebookresearch/detectron2
Author: FAIR
Author-email: None
License: UNKNOWN
Location: /home/ubuntu/anaconda3/envs/pytorch_p36/lib/python3.6/site-packages
Requires: yacs, tabulate, tqdm, pydot, tensorboard, Pillow, termcolor, future, cloudpickle, matplotlib, fvcore
Javi
fonte
0

Aqui está um script bash rápido, caso seja útil para qualquer pessoa. Eu só quero poder definir uma variável de ambiente para que eu possa pushdo código.

#!/bin/bash
module=${1:?"I need a module name"}

python << EOI
import $module
import os
print os.path.dirname($module.__file__)
EOI

Exemplo de shell:

[root@sri-4625-0004 ~]# export LXML=$(get_python_path.sh lxml)
[root@sri-4625-0004 ~]# echo $LXML
/usr/lib64/python2.7/site-packages/lxml
[root@sri-4625-0004 ~]#
shrewmouse
fonte