[Expanding my comment into an answer.]
One way is to write a function that takes a list of library names and installs them (if they are not installed already), then add the function to your init file. You can then use file-local variables or directory-local variables to define that list of library names for a specific file or a specific directory and evaluate the function you wrote with the list of library names as argument.
Here's a function, pip-package-install-list,that takes a list of library names and installs each one (if it is not installed already):
(defun pip-package-installedp (pkg) (message pkg) (= 0 (call-process "pip" nil nil nil "show" pkg))) (defun pip-package-install (pkg) (shell-command (format "pip install %s" pkg) nil nil)) (defun pip-package-install-list (l) "Install packages in the list L if not installed already." (dolist (pkg l) (unless (pip-package-installedp pkg) (message "Installing %s..." pkg) (pip-package-install pkg))))
It makes use of two helper functions, one to check whether the package is installed already and another to install it.
You need to put the above in your init file and restart your Emacs to ensure that the function is defined.
You can then add a local variables section to a Python file, so that when the file is opened, the local variables are evaluated as described in the links above. Here is a file that imports a couple of packages that are not normally installed (and one, xarray, that is):
#! /usr/bin/env python3 # -*- coding: utf-8 -*- """ Dummy """ # make pylint STFU about some things # pylint: disable-msg=C0103 import sys import argparse import xarray import foo12 import foo2bar def main(args): """ Main """ print(args) if args: return 0 return 1 if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument("-f", "--file", dest="filename", help="write report to FILE", metavar="FILE") parser.add_argument("-q", "--quiet", action="store_false", dest="verbose", default=True, help="don't print status messages to stdout") myargs = parser.parse_args() status = main(myargs) sys.exit(status) # Local variables: # liblist: ("foo12" # "xarray" # "foo2bar") # eval: (pip-package-install-list liblist) # End:
When you open the file, you are asked whether to allow the evaluation of the local variables and, assuming you accept, the liblist variable is set to the list you specify and the eval pseaudo variable causes the pip-packape-install-list function to be called with the liblist as argument, causing any missing packages to be installed. You can check by running the file from the command line and checking that no "missing package" errors are thrown.
Opening the *Messages* buffer and going to the end before opening the Python file will show you details of what is happening and what is being installed.
If you add another import to your python file and add that package to the liblist variable, you can re-evaluate the local variables by saying M-x normal-mode.
Using directory-local variables for a whole project is similar: see the appropriate link above.
requirements.txtfor your project?pipshould be able to use that to install the correct dependencies. This is more a question of python tooling, than emacs.