Bagaimana cara memanggil pip dari skrip python dan menginstalnya secara lokal ke skrip itu? [duplikat]

Kami memiliki skrip python di repositori kode sumber yang saya pelihara. Bayangkan saja itu ada di lokasi

scripts/python/make_salad/make_salad.py

Itu diperiksa ke dalam repositori apa adanya. Pengguna skrip hanya ingin mengklik skrip di windows explorer dan berangkat. Mereka menolak menggunakan baris perintah. Namun skripnya juga bergantung pada banyak paket eksternal yang harus saya instal. Saya telah menggunakan trik berikut untuk menginstal paket apa pun yang diperlukan saat pertama kali pengguna menjalankannya naskah. Ini seperti

def install(package):
    # This is an evil little function
    # that installs packages via pip.
    # This means the script can install
    # it's own dependencies.
    try:
        __import__(package)
    except:
        import subprocess
        subprocess.call([sys.executable, "-m", "pip", "install", package])

install("colorama")
install("pathlib")
install("iterfzf")
install("prompt_toolkit")
install("munch")
install("appdirs")
install("art")
install("fire")

import os
import tkFileDialog
import getpass
import json
import shutil
import subprocess
import sys
import pprint
import art

# <snip> out all my business logic
print("Making Salad")

Namun saya tidak suka ini karena menginstal paket ke repositori paket global. Yang saya inginkan adalah jika semua paket diinstal seperti ini

scripts/python/make_salad/make_salad.py
                         /__packages__
                                      /colorama
                                      /pathlib
                                      /iterfzf
                                      ...
                                      /fire

dan direktori ini akan menjadi yang pertama di jalur pencarian saat impor dipanggil. Bisakah skrip di atas diretas sehingga hal di atas bisa dilakukan?

Perhatikan persyaratannya

  1. Hanya satu skrip yang disimpan di repositori
  2. Pengguna harus mengklik skrip di windows explorer
  3. Perlu melakukan pip install paket eksternal dari dalam skrip
  4. Paket tidak boleh mencemari paket global

person bradgonesurfing    schedule 21.08.2019    source sumber
comment
Jadi, Anda ingin menginstal sesuatu di lingkungan virtual?   -  person 9769953    schedule 21.08.2019
comment
Ya, tapi saya tidak bisa membiarkan pengguna bermain-main di baris perintah atau mereka akan panik. Dan saya tidak dapat memeriksa lingkungan virtual apa pun ke dalam kontrol sumber atau membuat apa pun sebelumnya.   -  person bradgonesurfing    schedule 21.08.2019
comment
Apakah ada pembuat exe yang membantu dalam kasus ini (tergantung pada paketnya); di dalamnya ia membangun satu executable dari Python dan semua paket yang relevan.   -  person 9769953    schedule 21.08.2019
comment
Tidak. Saya tidak bisa memasukkan file executable besar ke dalam sumber   -  person bradgonesurfing    schedule 21.08.2019
comment
mungkin membuat venv dari skrip? docs.python.org/3/library/venv.html   -  person vinzBad    schedule 21.08.2019
comment
Pff, itu persyaratan yang konyol, kalau boleh kubilang begitu; dan bahkan dengan Python 2. Senang saya tidak di posisi Anda.   -  person 9769953    schedule 21.08.2019
comment
@vinzBad Persyaratannya (salah satunya) adalah Python 2.7; venv baru ada sejak Python 3.3, sesuai tautan Anda.   -  person 9769953    schedule 21.08.2019
comment
Saya bisa menjalankan virtualenv dari python2.7 melalui pip. Ini berfungsi cukup baik untuk membuat skrip dan sekarang saya memiliki solusi yang berfungsi. stackoverflow.com/a/57604352/158285   -  person bradgonesurfing    schedule 22.08.2019


Jawaban (2)


Ternyata tidak terlalu sulit menggunakan virtualenv langsung dari skrip untuk mengatasi masalah ini. Saya menulis kelas pembantu kecil.

import sys
import subprocess

class App:
    def __init__(self, virtual_dir):
        self.virtual_dir = virtual_dir
        self.virtual_python = os.path.join(self.virtual_dir, "Scripts", "python.exe")

    def install_virtual_env(self):
        self.pip_install("virtualenv")
        if not os.path.exists(self.virtual_python):
            import subprocess
            subprocess.call([sys.executable, "-m", "virtualenv", self.virtual_dir])
        else:
            print("found virtual python: " + self.virtual_python)

    def is_venv(self):
        return sys.prefix==self.virtual_dir

    def restart_under_venv(self):
        print("Restarting under virtual environment " + self.virtual_dir)
        subprocess.call([self.virtual_python, __file__] + sys.argv[1:])
        exit(0)

    def pip_install(self, package):
        try:
            __import__(package)
        except:
            subprocess.call([sys.executable, "-m", "pip", "install", package, "--upgrade"])

    def run(self):
        if not self.is_venv():
            self.install_virtual_env()
            self.restart_under_venv()
        else:
            print("Running under virtual environment")

Dan dapat menggunakannya dari atas skrip utama make_salad.py seperti itu

pathToScriptDir = os.path.dirname(os.path.realpath(__file__))

app = App(os.path.join(pathToScriptDir, "make_salad_virtual_env"))

app.run()

app.install("colorama")
app.install("pathlib")
app.install("iterfzf")
app.install("prompt_toolkit")
app.install("munch")
app.install("appdirs")
app.install("art")
app.install("fire")
app.install("appdirs")

print "making salad"

Pertama kali dijalankan akan menginstal virtual env dan semua paket yang diperlukan. Kedua kalinya itu hanya akan menjalankan skrip di bawah lingkungan virtual.

found virtual python: C:\workspace\make_salad_virtualenv\Scripts\python.exe
Restarting under virtual environment C:\workspace\make_salad_virtualenv\Scripts
Running under virtual environment
Making Salad
person bradgonesurfing    schedule 22.08.2019

Mungkin Anda dapat menggunakan opsi --prefix dengan pip, ubah sys.path untuk menyertakan awalan tersebut (+ lib/python2.7/site-packages/). Saya pikir --prefix bahkan akan bekerja dengan jalur relatif:

def install(package):
    try:
        __import__(package)
    except:
        import subprocess
        subprocess.call([sys.executable, "-m", "pip", "install", package, "--prefix="packages"])
.
.
.
sys.path.append("packages/lib/python2.7/site-packages/")
.
.
.
import art
.
.
.

(belum dicoba)

person 9769953    schedule 21.08.2019