如何从 python 脚本调用 pip 并使其在本地安装到该脚本?
How to call pip from a python script and make it install locally to that script?
我维护的源代码存储库中有一个 python 脚本。假设它在位置
scripts/python/make_salad/make_salad.py
它按原样签入存储库。该脚本的用户只想在 windows 资源管理器中单击该脚本,然后就可以离开了。他们拒绝使用命令行。但是,该脚本还依赖于我必须安装的许多外部包。我已经使用 following trick 来安装用户第一次运行脚本时所需的任何包。就像
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")
但是我不喜欢这样,因为它会将包安装到全局包存储库中。我想要的是,如果所有软件包都安装了
scripts/python/make_salad/make_salad.py
/__packages__
/colorama
/pathlib
/iterfzf
...
/fire
然后在调用导入时,此目录将位于搜索路径的第一位。是否可以破解上面的脚本,所以上面的是可能的?
注意要求
- 只有单个脚本存储在存储库中
- 用户必须在 windows 资源管理器
中单击脚本
- 需要从脚本中 pip 安装外部包
- 包不应该污染全局包
也许您可以将 --prefix
选项与 pip
一起使用,修改 sys.path
以包含该前缀 (+ lib/python2.7/site-packages/
)。我认为 --prefix
甚至可以使用相对路径:
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
.
.
.
(未经测试)
事实证明,直接从脚本中使用 virtualenv 来解决这个问题并不太难。我写了一个小帮手class.
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")
并且可以像这样从主脚本的顶部使用它make_salad.py
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"
第一次 运行ning 它将安装虚拟环境和所有必需的包。第二次它只是运行虚拟环境下的脚本。
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
我维护的源代码存储库中有一个 python 脚本。假设它在位置
scripts/python/make_salad/make_salad.py
它按原样签入存储库。该脚本的用户只想在 windows 资源管理器中单击该脚本,然后就可以离开了。他们拒绝使用命令行。但是,该脚本还依赖于我必须安装的许多外部包。我已经使用 following trick 来安装用户第一次运行脚本时所需的任何包。就像
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")
但是我不喜欢这样,因为它会将包安装到全局包存储库中。我想要的是,如果所有软件包都安装了
scripts/python/make_salad/make_salad.py
/__packages__
/colorama
/pathlib
/iterfzf
...
/fire
然后在调用导入时,此目录将位于搜索路径的第一位。是否可以破解上面的脚本,所以上面的是可能的?
注意要求
- 只有单个脚本存储在存储库中
- 用户必须在 windows 资源管理器 中单击脚本
- 需要从脚本中 pip 安装外部包
- 包不应该污染全局包
也许您可以将 --prefix
选项与 pip
一起使用,修改 sys.path
以包含该前缀 (+ lib/python2.7/site-packages/
)。我认为 --prefix
甚至可以使用相对路径:
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
.
.
.
(未经测试)
事实证明,直接从脚本中使用 virtualenv 来解决这个问题并不太难。我写了一个小帮手class.
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")
并且可以像这样从主脚本的顶部使用它make_salad.py
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"
第一次 运行ning 它将安装虚拟环境和所有必需的包。第二次它只是运行虚拟环境下的脚本。
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