5 from subprocess import check_call
8 def bootstrap_charm_deps():
10 Set up the base charm dependencies so that the reactive system can run.
12 venv = os.path.abspath('../.venv')
13 vbin = os.path.join(venv, 'bin')
14 vpip = os.path.join(vbin, 'pip')
15 vpy = os.path.join(vbin, 'python')
16 if os.path.exists('wheelhouse/.bootstrapped'):
17 from charms import layer
18 cfg = layer.options('basic')
19 if cfg.get('use_venv') and '.venv' not in sys.executable:
21 os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']])
22 reload_interpreter(vpy)
24 # bootstrap wheelhouse
25 if os.path.exists('wheelhouse'):
26 apt_install(['python3-pip', 'python3-yaml'])
27 from charms import layer
28 cfg = layer.options('basic')
29 # include packages defined in layer.yaml
30 apt_install(cfg.get('packages', []))
31 # if we're using a venv, set it up
32 if cfg.get('use_venv'):
33 apt_install(['python-virtualenv'])
34 cmd = ['virtualenv', '--python=python3', venv]
35 if cfg.get('include_system_packages'):
36 cmd.append('--system-site-packages')
38 os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']])
42 # save a copy of system pip to prevent `pip3 install -U pip` from changing it
43 if os.path.exists('/usr/bin/pip'):
44 shutil.copy2('/usr/bin/pip', '/usr/bin/pip.save')
45 # need newer pip, to fix spurious Double Requirement error https://github.com/pypa/pip/issues/56
46 check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse', 'pip'])
47 # install the rest of the wheelhouse deps
48 check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse'] + glob('wheelhouse/*'))
49 if not cfg.get('use_venv'):
50 # restore system pip to prevent `pip3 install -U pip` from changing it
51 if os.path.exists('/usr/bin/pip.save'):
52 shutil.copy2('/usr/bin/pip.save', '/usr/bin/pip')
53 os.remove('/usr/bin/pip.save')
54 # flag us as having already bootstrapped so we don't do it again
55 open('wheelhouse/.bootstrapped', 'w').close()
56 # Ensure that the newly bootstrapped libs are available.
57 # Note: this only seems to be an issue with namespace packages.
58 # Non-namespace-package libs (e.g., charmhelpers) are available
59 # without having to reload the interpreter. :/
60 reload_interpreter(vpy if cfg.get('use_venv') else sys.argv[0])
63 def reload_interpreter(python):
65 Reload the python interpreter to ensure that all deps are available.
67 Newly installed modules in namespace packages sometimes seemt to
68 not be picked up by Python 3.
70 os.execle(python, python, sys.argv[0], os.environ)
73 def apt_install(packages):
77 This ensures a consistent set of options that are often missed but
80 if isinstance(packages, (str, bytes)):
83 env = os.environ.copy()
85 if 'DEBIAN_FRONTEND' not in env:
86 env['DEBIAN_FRONTEND'] = 'noninteractive'
89 '--option=Dpkg::Options::=--force-confold',
92 check_call(cmd + packages, env=env)