python - poo - ¿Cómo determinar correctamente el directorio de scripts actual?
python poo tutorial (12)
Aquí hay una solución parcial, aún mejor que todas las publicadas hasta el momento.
import sys, os, os.path, inspect
#os.chdir("..")
if ''__file__'' not in locals():
__file__ = inspect.getframeinfo(inspect.currentframe())[0]
print os.path.dirname(os.path.abspath(__file__))
Ahora esto funciona todas las llamadas pero si alguien usa chdir()
para cambiar el directorio actual, esto también fallará.
Notas:
-
sys.argv[0]
no va a funcionar, devolverá-c
si ejecuta el script conpython -c "execfile(''path-tester.py'')"
- Publiqué una prueba completa en https://gist.github.com/1385555 y puede mejorarla.
Me gustaría ver cuál es la mejor manera de determinar el directorio actual de scripts en Python?
Descubrí que de dos a muchas formas de llamar al código python, es difícil encontrar una buena solución.
Aquí hay algunos problemas:
-
__file__
no está definido si el script se ejecuta conexec
,execfile
-
__module__
se define solo en módulos
Casos de uso:
-
./myfile.py
-
python myfile.py
-
./somedir/myfile.py
-
python somedir/myfile.py
-
execfile(''myfile.py'')
(desde otro script, que puede estar ubicado en otro directorio y que puede tener otro directorio actual.
Sé que no hay una solución perfecta, porque en algunos casos, pero estoy buscando el mejor enfoque que solucionó la mayoría de los casos.
El enfoque más utilizado es os.path.dirname(os.path.abspath(__file__))
pero esto realmente no funciona si ejecuta el script desde otro con exec()
.
Advertencia
Cualquier solución que use el directorio actual fallará, esto puede ser diferente según la forma en que se llama el script o puede modificarse dentro del script en ejecución.
El enfoque os.path...
fue lo ''hecho'' en Python 2.
En Python 3, puede encontrar el directorio del script de la siguiente manera:
from pathlib import Path
cwd = Path(__file__).parents[0]
Espero que esto ayude: - Si ejecuta un script / módulo desde cualquier lugar, podrá acceder a la variable __file__
, que es una variable de módulo que representa la ubicación del script.
Por otro lado, si está usando el intérprete, no tiene acceso a esa variable, donde obtendrá un nombre NameError
y os.getcwd()
le dará el directorio incorrecto si está ejecutando el archivo desde en algún otro lugar.
This solución debe darle lo que está buscando en todos los casos:
from inspect import getsourcefile
from os.path import abspath
abspath(getsourcefile(lambda:0))
No lo he probado a fondo, pero resolvió mi problema.
Esto debería funcionar en la mayoría de los casos:
import os,sys
dirname=os.path.dirname(os.path.realpath(sys.argv[0]))
Primero ... un par de casos de uso faltantes aquí si estamos hablando de formas de insertar código anónimo.
code.compile_command()
code.interact()
imp.load_compiled()
imp.load_dynamic()
imp.load_module()
__builtin__.compile()
loading C compiled shared objects? example: _socket?)
Pero, la verdadera pregunta es, ¿cuál es tu objetivo? ¿Estás tratando de imponer algún tipo de seguridad? ¿O solo te interesa qué se está cargando?
Si le interesa la security , el nombre de archivo que se está importando a través de exec / execfile es irrelevante; debe usar rexec , que ofrece lo siguiente:
Este módulo contiene la clase RExec, que admite los métodos r_eval (), r_execfile (), r_exec () y r_import (), que son versiones restringidas de las funciones estándar de Python eval (), execfile () y las instrucciones exec e import. El código ejecutado en este entorno restringido solo tendrá acceso a módulos y funciones que se consideren seguros; puede subclase RExec agregar o eliminar capacidades según lo desee.
Sin embargo, si esto es más una búsqueda académica ... aquí hay un par de enfoques tontos que quizás puedas profundizar un poco más en ...
Scripts de ejemplo:
./deep.py
print '' >> level 1''
execfile(''deeper.py'')
print '' << level 1''
./deeper.py
print ''/t >> level 2''
exec("import sys; sys.path.append(''/tmp''); import deepest")
print ''/t << level 2''
/tmp/deepest.py
print ''/t/t >> level 3''
print ''/t/t/t I can see the earths core.''
print ''/t/t << level 3''
./codespy.py
import sys, os
def overseer(frame, event, arg):
print "loaded(%s)" % os.path.abspath(frame.f_code.co_filename)
sys.settrace(overseer)
execfile("deep.py")
sys.exit(0)
Salida
loaded(/Users/synthesizerpatel/deep.py)
>> level 1
loaded(/Users/synthesizerpatel/deeper.py)
>> level 2
loaded(/Users/synthesizerpatel/<string>)
loaded(/tmp/deepest.py)
>> level 3
I can see the earths core.
<< level 3
<< level 2
<< level 1
Por supuesto, esta es una forma intensiva de recursos para hacerlo, estarías rastreando todo tu código ... No muy eficiente. Pero, creo que es un enfoque novedoso, ya que continúa funcionando incluso a medida que se adentra en el nido. No puede anular ''eval''. Aunque puede anular execfile ().
Tenga en cuenta que este enfoque solo codifica exec / execfile, no ''import''. Para un enganche de carga de ''nivel'' de nivel superior, es posible que pueda usar use sys.path_hooks (Cortesía de la compilación de PyMOTW).
Eso es todo lo que tengo fuera de mi cabeza.
Si realmente desea cubrir el caso de que un script se llame a través de execfile(...)
, puede usar el módulo de inspect
para deducir el nombre del archivo (incluida la ruta). Por lo que yo sé, esto funcionará para todos los casos que enumeró:
filename = inspect.getframeinfo(inspect.currentframe()).filename
path = os.path.dirname(os.path.abspath(filename))
Solo use os.path.dirname(os.path.abspath(__file__))
y examine muy cuidadosamente si existe una necesidad real para el caso en que se usa el exec
. Podría ser un signo de diseño problemático si no puede usar su secuencia de comandos como un módulo.
Tenga en cuenta Zen of Python # 8 , y si cree que hay un buen argumento para un caso de uso donde debe funcionar para el exec
, entonces háganos saber más detalles sobre el fondo del problema.
haría
import os
cwd = os.getcwd()
¿Haz lo que quieras? No estoy seguro de qué quiere decir exactamente con el "directorio actual de scripts". ¿Cuál sería el resultado esperado para los casos de uso que proporcionó?
#!/usr/bin/env python
import inspect
import os
import sys
def get_script_dir(follow_symlinks=True):
if getattr(sys, ''frozen'', False): # py2exe, PyInstaller, cx_Freeze
path = os.path.abspath(sys.executable)
else:
path = inspect.getabsfile(get_script_dir)
if follow_symlinks:
path = os.path.realpath(path)
return os.path.dirname(path)
print(get_script_dir())
Funciona en CPython, Jython, Pypy. Funciona si el script se ejecuta usando execfile()
( sys.argv[0]
y las __file__
basadas en __file__
fallarían aquí). Funciona si el script está dentro de un archivo zip ejecutable (/ un huevo) . Funciona si el script se "importa" ( PYTHONPATH=/path/to/library.zip python -mscript_to_run
) desde un archivo zip; devuelve la ruta del archivo en este caso. Funciona si el script se compila en un ejecutable independiente ( sys.frozen
). Funciona para enlaces simbólicos ( realpath
elimina enlaces simbólicos). Funciona en un intérprete interactivo; devuelve el directorio de trabajo actual en este caso.
import os
import sys
def get_script_path():
return os.path.dirname(os.path.realpath(sys.argv[0]))
my_script_dir = get_script_path()
print my_script_dir
Esto le da el directorio de la secuencia de comandos en la parte superior de la pila (es decir, el que se está ejecutando, no el de Python, que generalmente es el primero ejecutado, devolviendo C:/
)
os.path.dirname(os.path.abspath(__file__))
de hecho es lo mejor que vas a obtener.
No es habitual ejecutar una secuencia de comandos con exec
/ execfile
; normalmente debería usar la infraestructura del módulo para cargar scripts. Si debe usar estos métodos, le sugiero que configure __file__
en los globals
que pasa al script para que pueda leer ese nombre de archivo.
No hay otra manera de obtener el nombre del archivo en código ejecutado: como nota, el CWD puede estar en un lugar completamente diferente.