]> kaliko git repositories - mpd-sima.git/blob - vinstall.py
New virtualenv installer in pure python
[mpd-sima.git] / vinstall.py
1 #
2 # Copyright (C) 2013 Vinay Sajip. New BSD License.
3 # Copyright (C) 2014 Kaliko Jack
4 #
5 REQ_VER = (3,3)
6 import sys
7 if sys.version_info < REQ_VER:
8     print('Need at least python {0}.{1} to run this script'.format(*REQ_VER), file=sys.stderr)
9     sys.exit(1)
10
11 import os
12 import os.path
13 import venv
14
15 from subprocess import Popen, PIPE
16 from threading import Thread
17 from urllib.parse import urlparse
18 from urllib.request import urlretrieve
19 from shutil import rmtree
20
21 class ExtendedEnvBuilder(venv.EnvBuilder):
22     """
23     This builder installs setuptools and pip so that you can pip or
24     easy_install other packages into the created environment.
25     """
26
27     def __init__(self, *args, **kwargs):
28         self.verbose = kwargs.pop('verbose', False)
29         super().__init__(*args, **kwargs)
30
31     def post_setup(self, context):
32         """
33         Set up any packages which need to be pre-installed into the
34         environment being created.
35
36         :param context: The information for the environment creation request
37                         being processed.
38         """
39         os.environ['VIRTUAL_ENV'] = context.env_dir
40         self.install_setuptools(context)
41         self.install_pip(context)
42         setup = os.path.abspath(os.path.join(context.env_dir, '../setup.py'))
43         self.install_script(context, 'sima', setup=setup)
44
45     def reader(self, stream, context):
46         """
47         Read lines from a subprocess' output stream and write progress
48         information to sys.stderr.
49         """
50         while True:
51             s = stream.readline()
52             if not s:
53                 break
54             if not self.verbose:
55                 sys.stderr.write('.')
56             else:
57                 sys.stderr.write(s.decode('utf-8'))
58             sys.stderr.flush()
59         stream.close()
60
61     def install_script(self, context, name, url=None, setup=None):
62         if url:
63             binpath = context.bin_path
64             _, _, path, _, _, _ = urlparse(url)
65             fn = os.path.split(path)[-1]
66             distpath = os.path.join(binpath, fn)
67             # Download script into the env's binaries folder
68             urlretrieve(url, distpath)
69         if url:
70             args = [context.env_exe, fn]
71         else:
72             args = [context.env_exe, setup, 'install']
73             binpath = os.path.dirname(setup)
74         if self.verbose:
75             term = '\n'
76         else:
77             term = ''
78         sys.stderr.write('Installing %s ...%s' % (name, term))
79         sys.stderr.flush()
80         # Install in the env
81         p = Popen(args, stdout=PIPE, stderr=PIPE, cwd=binpath)
82         t1 = Thread(target=self.reader, args=(p.stdout, 'stdout'))
83         t1.start()
84         t2 = Thread(target=self.reader, args=(p.stderr, 'stderr'))
85         t2.start()
86         p.wait()
87         t1.join()
88         t2.join()
89         sys.stderr.write('done.\n')
90         if url:
91             # Clean up - no longer needed
92             os.unlink(distpath)
93
94     def install_setuptools(self, context):
95         """
96         Install setuptools in the environment.
97
98         :param context: The information for the environment creation request
99                         being processed.
100         """
101         url = 'https://bitbucket.org/pypa/setuptools/downloads/ez_setup.py'
102         self.install_script(context, 'setuptools', url)
103         # clear up the setuptools archive which gets downloaded
104         pred = lambda o: o.startswith('setuptools-') and o.endswith('.tar.gz')
105         files = filter(pred, os.listdir(context.bin_path))
106         for f in files:
107             f = os.path.join(context.bin_path, f)
108             os.unlink(f)
109
110     def install_pip(self, context):
111         """
112         Install pip in the environment.
113
114         :param context: The information for the environment creation request
115                         being processed.
116         """
117         url = 'https://raw.github.com/pypa/pip/master/contrib/get-pip.py'
118         self.install_script(context, 'pip', url)
119         # pip installs to "local/bin" on Linux, but it needs to be accessible
120         # from "bin" since the "activate" script prepends "bin" to $PATH
121         pip_path = os.path.join(context.env_dir, 'local', 'bin', 'pip')
122         if sys.platform != 'win32' and os.path.exists(pip_path):
123             self.symlink_or_copy(pip_path, os.path.join(context.bin_path, 'pip'))
124
125
126 def main(args=None):
127     root = os.path.dirname(os.path.abspath(__file__))
128     vdir = os.path.join(root, 'venv')
129     builder = ExtendedEnvBuilder(clear=True, verbose=False)
130     builder.create(vdir)
131     # clean up
132     for residu in ['MPD_sima.egg-info', 'dist', 'build']:
133         if os.path.exists(os.path.join(root, residu)):
134             rmtree(os.path.join(root, residu))
135     # Write wrapper
136     with open(os.path.join(root, 'vmpd-sima'),'w') as fd:
137         fd.write('#!/bin/sh\n')
138         fd.write('. "{}/venv/bin/activate"\n'.format(root))
139         fd.write('"{}/venv/bin/mpd-sima" "$@"'.format(root))
140     os.chmod(os.path.join(root, 'vmpd-sima'), 0o744)
141
142 if __name__ == '__main__':
143     rc = 1
144     try:
145         main()
146         rc = 0
147     except ImportError as e:
148         print('Error: %s' % e)
149     sys.exit(rc)