Python: la mejor manera de agregar a sys.path en relación con el script en ejecución actual

99

Tengo un directorio lleno de scripts (digamos project/bin). También tengo una biblioteca ubicada project/liby quiero que los scripts la carguen automáticamente. Esto es lo que normalmente uso en la parte superior 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

Esto es algo engorroso, feo y debe pegarse al principio de cada archivo. ¿Hay una mejor manera de hacer esto?

Realmente lo que espero es algo tan suave como esto:

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

import mylib

O incluso mejor, algo que no se rompería cuando mi editor (o alguien más que tenga acceso de confirmación) decida reordenar las importaciones como parte de su proceso de limpieza:

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

Eso no se trasladaría directamente a plataformas que no sean posix, pero mantendría las cosas limpias.

James Harr
fuente
1
Ver también: stackoverflow.com/questions/2349991/…
dreftymac

Respuestas:

26

Si no desea editar cada archivo

  • Instale su biblioteca como una biblioteca Python normal
    o
  • Establecer PYTHONPATHa sulib

o si está dispuesto a agregar una sola línea a cada archivo, agregue una declaración de importación en la parte superior, por ejemplo

import import_my_lib

manténgalo import_my_lib.pyen la papelera y import_my_libpuede configurar correctamente la ruta de Python a lo libque desee

Anurag Uniyal
fuente
118

Esto es lo que uso:

import os, sys
sys.path.append(os.path.join(os.path.dirname(__file__), "lib"))
terraza
fuente
2
Haría sys.path.insert (0, ..) para que no sea anulado por otras rutas.
John Jiang
¿Realmente no hay forma de que esto se ejecute automáticamente?
Denis de Bernardy
1
Esto es un poco arriesgado. Si __file__es un nombre de archivo relativo al directorio de trabajo actual (por ejemplo, setup.py), entonces os.path.dirname(__file__)será la cadena vacía. Por esta y preocupaciones similares planteada por John Jiang , ekhumoro Es más solución de propósito general es mucho más preferible.
Cecil Curry
29

Estoy usando:

import sys,os
sys.path.append(os.getcwd())
DusX
fuente
5
A menudo lo hagosys.path.append('.')
kimbo
1
¿Qué pasa si el script se ejecuta desde un directorio diferente? por ejemplo, desde el directorio raíz dando la ruta completa del sistema? luego os.getcwd () devolverá "/"
obayhan
2
Nunca hagas esto. No se garantiza que el directorio de trabajo actual (CWD) sea ​​lo que usted cree que es, especialmente en casos extremos imprevisibles que definitivamente debería haber visto venir a una milla de distancia. En su __file__lugar, solo haga referencia como cualquier desarrollador cuasi sano.
Cecil Curry
14

Cree un módulo contenedor project/bin/lib, que contenga esto:

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

Luego puede reemplazar todo el cruft en la parte superior de sus scripts con:

#!/usr/bin/python
from lib import mylib
ekhumoro
fuente
9

Si no desea cambiar el contenido del script de ninguna manera, anteponga el directorio de trabajo actual .a $ PYTHONPATH (vea el ejemplo a continuación)

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

¡Y llámalo un día!

Khanh Hua
fuente
docs.python.org/3/tutorial/modules.html#the-module-search-path dice: "sys.path se inicializa desde estas ubicaciones: -El directorio que contiene el script de entrada". Creo que esto no es cierto.
Tiendo a hacer esto, especialmente en .envrc para que con direnv sea incluso automático y aislado también.
EdvardM
8

Usando python 3.4+ Excluyendo
el uso de cx_freeze o usando en IDLE. 😃

import sys
from pathlib import Path

sys.path.append(Path(__file__).parent / "lib")
GollyJer
fuente
Compatible con Python2: importar pathlib import os sys.path.append (os.path.dirname ( archivo ))
michael
5

Puede ejecutar el script python -mdesde el directorio raíz correspondiente. Y pase la "ruta de los módulos" como argumento.

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


Otro ejemplo:

$ 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
fuente
1
Usar el conmutador m es la forma recomendada de hacer esto y no los
trucos de
4

Hay un problema con cada respuesta proporcionada que se puede resumir como "simplemente agregue este encantamiento mágico al principio de su script. Vea lo que puede hacer con solo una línea o dos de código". ¡No funcionarán en todas las situaciones posibles!

Por ejemplo, uno de esos encantamientos mágicos usa file . Desafortunadamente, si empaqueta su script usando cx_Freeze o está usando IDLE, esto resultará en una excepción.

Otro encantamiento mágico de este tipo usa os.getcwd (). Esto solo funcionará si está ejecutando su script desde el símbolo del sistema y el directorio que contiene su script es el directorio de trabajo actual (es decir, usó el comando cd para cambiar al directorio antes de ejecutar el script). ¡Eh, dioses! Espero no tener que explicar por qué esto no funcionará si su script de Python está en la RUTA en algún lugar y lo ejecutó simplemente escribiendo el nombre de su archivo de script.

Afortunadamente, hay un encantamiento mágico que funcionará en todos los casos que he probado. Desafortunadamente, el encantamiento mágico es más que una o dos líneas 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 puede ver, ¡no es una tarea fácil!

Ben Key
fuente
0

Este funciona mejor para mí. Utilizar:

os.path.abspath('')

En mac debería imprimir algo como:

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

Para obtener la ruta de abs al wd actual, este es mejor porque ahora puede subir si lo desea, así:

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

Y ahora:

 '/Users/<your username>/'

Entonces, si quieres importar utilsdesde aquí, '/Users/<your username>/'
todo lo que te queda por hacer es:

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

Veo un shebang en tu ejemplo. Si está ejecutando sus scripts bin como ./bin/foo.py, en lugar de python ./bin/foo.py, hay una opción de usar el shebang para cambiar$PYTHONPATH variable.

Sin embargo, no puede cambiar las variables de entorno directamente en shebangs, por lo que necesitará un pequeño script de ayuda. Pon esto python.shen tu bincarpeta:

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

Y luego cambia el shebang de tu ./bin/foo.pypara ser#!bin/python.sh

imbolc
fuente
0

Cuando intentamos ejecutar un archivo python con la ruta desde la 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)

Guarde el archivo (test.py).

Sistema de funcionamiento.

Abra el terminal y vaya al directorio donde está el archivo de guardado. a continuación, escribir

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

Pulsa Enter

Salida:

File Name: test, Argument dir: /home/saiful/Desktop/bird.jpg
islam feliz
fuente
0

Yo suelo:

from site import addsitedir

¡Entonces, puede usar cualquier directorio relativo! addsitedir('..\lib') ; los dos puntos implican mover (arriba) un directorio primero.

Recuerde que todo depende de cuál sea su directorio de trabajo actual desde el que comience. Si C: \ Joe \ Jen \ Becky, entonces addeditedir ('.. \ lib') importa a su ruta C: \ Joe \ Jen \ lib

C:\
  |__Joe
      |_ Jen
      |     |_ Becky
      |_ lib
usuario7693644
fuente
Agradezco la respuesta, pero esto no agrega una ruta relativa al script en ejecución actual. Agrega una ruta relativa al directorio de trabajo actual
James Harr