Reputation: 43
I am seeking to make a script I have written more portable.
Several of the tools used in my script require that certain modules be installed. I have been able to have the script install the modules on its own, but would like to have a mechanism that would check if they are already installed and only attempt to install if missing.
I am sure this is possible, but I cannot seem to find a solution.
Upvotes: 4
Views: 5378
Reputation: 40891
Normally, it's not nice to download and install things on behalf of somebody running your script. As triplee's answer mentions, using a requirements.txt and a proper setup.py is a standard and far better practice.
At any rate, the following is a hack to get the behavior you want in a script.
import pip
import importlib
modules = ['requests', 'fake_module_name_that_does_not_exist']
for modname in modules:
try:
# try to import the module normally and put it in globals
globals()[modname] = importlib.import_module(modname)
except ImportError as e:
result = pip.main(['install', modname])
if result != 0: # if pip could not install it reraise the error
raise
else:
# if the install was sucessful, put modname in globals
globals()[modname] = importlib.import_module(modname)
If you were to execute this example, you would get output something like as follows
Collecting requests
Using cached requests-2.18.4-py2.py3-none-any.whl
Requirement already satisfied: idna<2.7,>=2.5 in c:\users\spenceryoung\envs\test_venv\lib\site-packages (from requests)
Requirement already satisfied: urllib3<1.23,>=1.21.1 in c:\users\spenceryoung\envs\test_venv\lib\site-packages (from requests)
Requirement already satisfied: chardet<3.1.0,>=3.0.2 in c:\users\spenceryoung\envs\test_venv\lib\site-packages (from requests)
Requirement already satisfied: certifi>=2017.4.17 in c:\users\spenceryoung\envs\test_venv\lib\site-packages (from requests)
Installing collected packages: requests
Successfully installed requests-2.18.4
Collecting fake_module_name_that_does_not_exist
Could not find a version that satisfies the requirement fake_module_name_that_does_not_exist (from versions: )
No matching distribution found for fake_module_name_that_does_not_exist
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
File "C:\Users\spenceryoung\AppData\Local\Programs\Python\Python36-32\lib\importlib\__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 994, in _gcd_import
File "<frozen importlib._bootstrap>", line 971, in _find_and_load
File "<frozen importlib._bootstrap>", line 953, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'fake_module_name_that_does_not_exist'
Upvotes: 2
Reputation: 189387
The standard solution to this is to make your script into a module, and declare its dependencies in the setup.py
of the module.
from setuptools import setup
setup(name='foo',
...
install_requires=['dependency', 'other', ...])
A supporting, weaker convention is to keep a list in requirements.txt
so you can
pip install -r requirements.txt
Having your own script do these chores is not usually useful or necessary; the existing packaging infrastructure already provides what you need, and is the natural point to manipulate and manage package dependencies.
Historically, there was some turmoil as different packaging regimes competed for dominance in the Python ecosystem, but things seem to have pretty much settled on setuptools
and pip
now.
Upvotes: 3
Reputation: 1529
You can try with this:
import pip
def install(package):
pip.main(['install', package])
try:
import your_module
except ImportError:
print 'Module not installed'
install('your_module')
Upvotes: 3