#!/usr/bin/env python3 import os import sys import argparse import subprocess import glob from importlib import import_module CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) ZULIP_BOTS_DIR = os.path.join(CURRENT_DIR, '..', 'zulip_bots') sys.path.append(ZULIP_BOTS_DIR) red = '\033[91m' green = '\033[92m' end_format = '\033[0m' bold = '\033[1m' def main(): usage = """./tools/provision Creates a Python virtualenv. Its Python version is equal to the Python version this command is executed with.""" parser = argparse.ArgumentParser(usage=usage) parser.add_argument('--python-interpreter', '-p', metavar='PATH_TO_PYTHON_INTERPRETER', default=os.path.abspath(sys.executable), help='Path to the Python interpreter to use when provisioning.') parser.add_argument('--force', '-f', action='store_true', help='create venv even with outdated Python version.') options = parser.parse_args() base_dir = os.path.abspath(os.path.join(__file__, '..', '..')) py_version_output = subprocess.check_output([options.python_interpreter, '--version'], stderr=subprocess.STDOUT, universal_newlines=True) # The output has the format "Python 1.2.3" py_version_list = py_version_output.split()[1].split('.') py_version = tuple(int(num) for num in py_version_list[0:2]) venv_name = 'zulip-api-py{}-venv'.format(py_version[0]) if py_version <= (3, 1) and (not options.force): print(red + "Provision failed: Cannot create venv with outdated Python version ({}).\n" "Maybe try `python3 tools/provision`." .format(py_version_output.strip()) + end_format) sys.exit(1) venv_dir = os.path.join(base_dir, venv_name) if not os.path.isdir(venv_dir): try: return_code = subprocess.call(['virtualenv', '-p', options.python_interpreter, venv_dir]) except OSError: if subprocess.call(['which', 'virtualenv']): print("{red}Please install the virtualenv package and try again.{end_format}" .format(red='\033[91m', end_format='\033[0m')) sys.exit(1) raise else: if return_code: raise OSError("The command `virtualenv -p {} {}` failed. Virtualenv not created!" .format(options.python_interpreter, venv_dir)) print("New virtualenv created.") else: print("Virtualenv already exists.") if os.path.isdir(os.path.join(venv_dir, 'Scripts')): # POSIX compatibility layer and Linux environment emulation for Windows # Virtual uses /Scripts instead of /bin on Windows. # Read https://virtualenv.pypa.io/en/stable/userguide/ venv_exec_dir = 'Scripts' else: venv_exec_dir = 'bin' # In order to install all required packages for the venv, we need to activate it. Since # the activation script sets environmental variables, it needs to be executed inline with # `import_module`. activate_module_dir = os.path.abspath(os.path.join(venv_dir, venv_exec_dir)) sys.path.append(activate_module_dir) import_module('activate_this') # In order to install all required packages for the venv, `pip` needs to be executed by # the venv's Python interpreter. `--prefix venv_dir` ensures that all modules are installed # in the right place. def install_dependencies(requirements_filename): pip_path = os.path.join(venv_dir, venv_exec_dir, 'pip') # We first install a modern version of pip that supports --prefix subprocess.call([pip_path, 'install', 'pip>=9.0']) if subprocess.call([pip_path, 'install', '--prefix', venv_dir, '-r', os.path.join(base_dir, requirements_filename)]): raise OSError("The command `pip install -r {}` failed. Dependencies not installed!" .format(os.path.join(base_dir, requirements_filename))) install_dependencies('requirements.txt') if py_version > (3, 1): install_dependencies('py3_requirements.txt') # Install all requirements for all bots. get_bot_paths() # has requirements that must be satisfied prior to calling # it by setup(). current_dir = os.path.dirname(os.path.abspath(__file__)) bots_dir = os.path.join(current_dir, "..", "zulip_bots", "zulip_bots", "bots") req_paths = glob.glob(bots_dir + "/*/requirements.txt") for req_path in req_paths: path_split = req_path.split(os.path.sep)[-5:] relative_path = os.path.join(*path_split) install_dependencies(relative_path) print(green + 'Success!' + end_format) activate_command = os.path.join(base_dir, venv_dir, venv_exec_dir, 'activate') print('\nRun the following the enter the virtualenv:\n') print(bold + ' source ' + activate_command + end_format + "\n") if __name__ == '__main__': main()