Gostaria de ver qual é a melhor maneira de determinar o diretório de script atual em python?
Descobri que, devido às muitas maneiras de chamar código python, é difícil encontrar uma boa solução.
Aqui estão alguns problemas:
__file__
não está definido se o script for executado comexec
,execfile
__module__
é definido apenas em módulos
Casos de uso:
./myfile.py
python myfile.py
./somedir/myfile.py
python somedir/myfile.py
execfile('myfile.py')
(de outro script, que pode estar localizado em outro diretório e que pode ter outro diretório atual.
Sei que não existe uma solução perfeita, mas estou procurando a melhor abordagem que resolva a maioria dos casos.
A abordagem mais usada é, os.path.dirname(os.path.abspath(__file__))
mas isso realmente não funciona se você executar o script de outra com exec()
.
Aviso
Qualquer solução que use o diretório atual falhará, isso pode ser diferente com base na maneira como o script é chamado ou pode ser alterado dentro do script em execução.
python
pythonpath
dirname
Bogdan
fonte
fonte
pathlib
solução de Ron Kalian se você estiver usando o python 3.4 ou superior: stackoverflow.com/a/48931294/1011724python myfile.py
partir de um shell, ele funciona, mas ambos:!python %
e:!python myfile.py
de dentro do vim falham com O sistema não consegue encontrar o caminho especificado. Isso é muito chato. Alguém pode comentar sobre o motivo por trás disso e possíveis soluções alternativas?Respostas:
é realmente o melhor que você vai conseguir.
É incomum executar um script com
exec
/execfile
; normalmente você deve usar a infraestrutura do módulo para carregar scripts. Se você deve usar esses métodos, sugiro definir__file__
a opção deglobals
passar para o script para que ele possa ler esse nome de arquivo.Não há outra maneira de obter o nome do arquivo no código executado: como você observa, o CWD pode estar em um local completamente diferente.
fonte
Se você realmente deseja cobrir o caso pelo qual um script é chamado
execfile(...)
, você pode usar oinspect
módulo para deduzir o nome do arquivo (incluindo o caminho). Tanto quanto sei, isso funcionará para todos os casos que você listou:fonte
chdir()
antes da função, isso mudará o resultado. Também chamar o script python de outro diretório alterará o resultado, portanto não é uma boa solução.os.path.expanduser("~")
é uma maneira multiplataforma de obter o diretório do usuário. Infelizmente, essa não é a melhor prática do Windows para onde colar dados de aplicativos.chdir()
antes de executar o script; produz resultado correto. Eu tentei chamar o script de outro diretório e também funciona. Os resultados são os mesmosinspect.getabsfile()
da solução baseada em .Funciona em CPython, Jython, Pypy. Funciona se o script for executado usando
execfile()
(as soluções baseadas emsys.argv[0]
e__file__
falhariam aqui). Funciona se o script estiver dentro de um arquivo zip executável (/ um ovo) . Funciona se o script for "importado" (PYTHONPATH=/path/to/library.zip python -mscript_to_run
) de um arquivo zip; retorna o caminho do arquivo morto neste caso. Funciona se o script for compilado em um executável independente (sys.frozen
). Funciona para links simbólicos (realpath
elimina links simbólicos). Funciona em um intérprete interativo; retorna o diretório de trabalho atual neste caso.fonte
getabsfile(..)
não ser mencionado na documentaçãoinspect
? Ele aparece na fonte vinculada a essa página.getsourcefile()
,getfile()
documentado.No Python 3.4+, você pode usar o
pathlib
módulo mais simples :fonte
Path(__file__)
(não há necessidade doinspect
módulo).Path(__file__)
dá/path/to/script/currentscript.py
quando o OP queria chegar/path/to/script/
parent = Path(__file__).resolve().parent
Isso é muito melhor..joinpath()
(ou o/
operador) para isso, não+
.A
os.path...
abordagem foi a 'coisa feita' no Python 2.No Python 3, você pode encontrar o diretório do script da seguinte maneira:
fonte
Path(__file__).parent
. Mascwd
é um nome impróprio, esse não é o diretório de trabalho atual , mas o diretório do arquivo . Eles podem ser os mesmos, mas geralmente não é o caso.Basta usar
os.path.dirname(os.path.abspath(__file__))
e examinar com muito cuidado se existe uma necessidade real do caso em queexec
é usado. Pode ser um sinal de design problemático se você não conseguir usar o script como um módulo.Lembre-se do Zen do Python # 8 , e se você acredita que há um bom argumento para um caso de uso em que ele deve funcionar
exec
, informe-nos mais alguns detalhes sobre o plano de fundo do problema.fonte
Seria
faça o que você quiser? Não sei o que exatamente você quer dizer com o "diretório de scripts atual". Qual seria a saída esperada para os casos de uso que você forneceu?
fonte
exec('myfile.py')
, o mesmo que__file__
esys.argv[0]
.Primeiro .. alguns casos de uso ausentes aqui, se estamos falando de maneiras de injetar código anônimo ..
Mas, a verdadeira questão é: qual é o seu objetivo - você está tentando impor algum tipo de segurança? Ou você está apenas interessado no que está sendo carregado.
Se você estiver interessado em segurança , o nome do arquivo que está sendo importado via exec / execfile é irrelevante - você deve usar o rexec , que oferece o seguinte:
No entanto, se isso é mais uma busca acadêmica ... aqui estão algumas abordagens patetas que você poderá aprofundar um pouco ..
Scripts de exemplo:
./deep.py
./deeper.py
/tmp/deepest.py
./codespy.py
Resultado
Obviamente, essa é uma maneira que exige muitos recursos, pois você rastreará todo o seu código. Não é muito eficiente. Mas acho que é uma abordagem inovadora, pois continua a funcionar mesmo quando você se aprofunda no ninho. Você não pode substituir 'eval'. Embora você possa substituir execfile ().
Observe que essa abordagem abrange apenas exec / execfile, não 'importação'. Para um gancho de carga de 'módulo' de nível superior, você pode usar o uso sys.path_hooks (gravação cortesia de PyMOTW).
É tudo o que tenho em cima da minha cabeça.
fonte
Aqui está uma solução parcial, ainda melhor do que todas as publicadas até agora.
Agora isso funciona todas as chamadas, mas se alguém usar
chdir()
para alterar o diretório atual, isso também falhará.Notas:
sys.argv[0]
não funcionar, retornará-c
se você executar o script compython -c "execfile('path-tester.py')"
fonte
Isso deve funcionar na maioria dos casos:
fonte
Espero que isso ajude: - Se você executar um script / módulo de qualquer lugar, poderá acessar a
__file__
variável, que é uma variável de módulo que representa a localização do script.Por outro lado, se você estiver usando o intérprete, não terá acesso a essa variável, onde receberá um nome
NameError
eos.getcwd()
fornecerá o diretório incorreto se estiver executando o arquivo em outro local.Esta solução deve fornecer o que você procura em todos os casos:
Ainda não o testei completamente, mas resolveu o meu problema.
fonte