2015-03-31 11 views
7

Czy istnieje sposób modyfikowania Pythona/PIP do, gdy import nie powiedzie się w runtime, to spróbuje zainstalować moduł (o tej samej nazwie) z pip a następnie zaimportować moduł?Modyfikuj Pythona/PIP, aby automatycznie instalować moduły, gdy nie udało się zaimportować

Powiedziałbym, że byłoby to ładniejsze domyślne niż po prostu wrzucić błąd. Jeśli po załadowaniu modułu z pip wystąpią jakiekolwiek problemy, to również wystąpiłby błąd, podobny do tego, gdy zauważyłem, że nie mogę zaimportować czegoś, spróbuj pip install, a następnie przejdź do dokładnie ten sam komunikat o błędzie.

Wiem, że możemy użyć requirements.txt do pakowania pakietu, ale mówię od "klienta" (osoby uruchamiającej skrypt), a nie "dostawcy" (osoba dostarczająca skrypt) z perspektywy; to znaczy, ja, jako klient, chciałbym móc importować dowolny skrypt i mieć zależności automatycznie rozwiązywane.

Rozumiem, że może to potencjalnie powodować problemy, ale gdy widzę wyjątku ImportError, powinienem spróbować mimo wszystko wykonać moduł pip install. Tylko jeśli moduł nie działałby po instalacji pipowej, "czy mógłbym zadać dalsze pytania".

Myślałem o coś takiego fragmentu, który byłby „zbudowany” do procesu Pythona:

def getDirectoryOfInterpreter(p): 
    return "someway to return the directory" 

try: 
    import somemodule 
except ImportError: 
    os.system(getDirectoryOfInterpreter('THIS_INTERPRETER_BINARY') + ' pip install ' + "MODULE_NAME") 
    import somemodule 
+0

Tak. Tak dzieje się w Python Packages, kiedy są instalowane, sprawdzają zależności i instalują pozostałe pakiety. –

+0

Mówię o rozwiązywaniu zależności w czasie wykonywania skryptu. – PascalVKooten

+0

Mogę mieć sposób, aby ci pomóc, ale zależność powinna być zainstalowana przed –

Odpowiedz

11

Można to zrobić z pipimport, przy użyciu virtualenv. Prawdopodobnie działa z pytonem systemowym, jeśli masz odpowiednie uprawnienia do zapisu niezbędnych katalogów (co najmniej site-packages, ale twój import może mieć jakieś polecenie, które pip spróbuje umieścić gdzieś w PATH). Ponieważ dobrze jest zawsze używać virtualenvs dla własnego rozwoju, nigdy nie próbowałem używać pipimport z pytonem systemowym.

trzeba importować pipimport do swojej virtualenv ręcznie:

virtualenv venv 
source venv/bin/activate 
pip install pipimport 

Następnie należy utworzyć plik autopipimport.py że import pierwszy w dowolnym module:

# import and install pipimport 
import pipimport 
pipimport.install() 

Teraz w każdy inny plik .py możesz do:

import autopipimport 
import some.package.installable.by.pip 

Raz spróbowałem (auto z ciekawość), aby dodać dwie linie powiązane z pipimportem do venv/lib/python2.7/site.py, aby automatycznie "załadować", ale to było na początku łańcucha i nie działało.

pipimport spróbuje użyć pip tylko do zainstalowania danego modułu raz, przechowywanie informacji na temat tego, co zostało próbowałem w pliku .pipimport-ignore (powinny być pod venv, chyba że nie jest zapisywalny).

+2

Należy zauważyć, że wymaga to nazwy pakietu odpowiadającej nazwie projektu w PyPI; 'from bs4 import BeautifulSoup' nie znajdzie projektu' beautifulsoup4', w którym jest zarejestrowany. –

+0

Chociaż jest to prawda w ogóle, na podstawie ograniczenia wprowadzonego przez OP do tego pytania, jest ono podane. – Anthon

+0

@MartijnPieters Miałem także dokładnie to na myśli, chociaż jest raczej wyjątkiem. Czy możesz wymyślić inny? Tak czy inaczej, celem nie jest możliwość liczyć na brak błędów, ale ograniczysz ich liczbę. – PascalVKooten

0

Oto coś, co zrobiłem dla własnej ciekawości.

Tak, to niesłusznie wywołuje powłokę w tle z dowolnym wejściem użytkownika przy użyciu innej niż preferowana funkcji. Nie używaj tego kodu do produkcji.

import os 

Libraries_Installed = False 

def Pip_Install(library): 
    """Locate pip.exe on system and install library. 
    Be sure to sanitize input to prevent RCE.""" 
    global Lib_Installed 

    if os.path.isfile('C:\\Python27\\Scripts\\pip.exe'): 
     print '[!] c:\Python27\Scripts\pip.exe => pip' 
     stream = os.popen('C:\Python27\Scripts\pip.exe install %s' % library) 
     Result = stream.read() 

     if 'Successfully installed' in str(Result): 
      print '\n\n[+] Successfully installed %s' % library 
      Lib_Installed = True 

     elif 'FAIL' in str(Result).upper() or 'FAILED' in str(Result).upper() \ 
     or 'FAILURE' in str(Result).upper(): 
      print '[!] Unable to install %d' % library 
      print '[!] Please manually run the pip installer' 
      print '[!] try: C:\Python27\Scripts\pip.exe install %s' % library 

    elif os.path.isfile('D:\\Python27\\Scripts\\pip.exe'): 
     stream = os.popen('D:\Python27\Scripts\pip.exe install %s' % library) 
     Result = stream.read() 

     if 'Successfully installed' in str(Result): 
      print '\n\n[+] Successfully installed %s' % library 
      Lib_Installed = True 

     elif 'FAIL' in str(Result).upper() or 'FAILED' in str(Result).upper() \ 
     or 'FAILURE' in str(Result).upper(): 
      print '[!] Unable to install %d' % library 
      print '[!] Please manually run the pip installer' 
      print '[!] try: C:\Python27\Scripts\pip.exe install %s' % library 

# Python library that may need to be installed 
try: 
    import Tkinter 
    import tkFileDialog 
except: 
    print '[!] Error importing Tkinter ... Trying pip installer' 
    Pip_Install('tkinter') 

if Lib_Installed == True: 
    print '\n[!] The following Python libraries were installed automatically:' 
    for library in Libs: 
     print str(library) 
    Q = raw_input('''\n[!] Please exit the program and run a new instance. 
[!] Enter [Y] to continue anyways''') 
    if Q.upper()[0] == 'Y': 
     pass 
    else: 
     exit() 
+1

Tak, to niesłusznie wywołuje powłokę w tle z dowolnym wejściem użytkownika przy użyciu innej niż preferowana funkcji. Nie używaj tego kodu do produkcji. – Injectnique

Powiązane problemy