Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2b4fcfb

Browse files
committed
Updated venv documentation with an example.
1 parent d1da29c commit 2b4fcfb

1 file changed

Lines changed: 213 additions & 0 deletions

File tree

Doc/library/venv.rst

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,216 @@ There is also a module-level convenience function:
178178

179179
Create an :class:`EnvBuilder` with the given keyword arguments, and call its
180180
:meth:`~EnvBuilder.create` method with the *env_dir* argument.
181+
182+
An example of extending ``EnvBuilder``
183+
--------------------------------------
184+
185+
The following script shows how to extend :class:`EnvBuilder` by implementing a
186+
subclass which installs Distribute and pip into a created venv::
187+
188+
import os
189+
import os.path
190+
from subprocess import Popen, PIPE
191+
import sys
192+
from threading import Thread
193+
from urllib.parse import urlparse
194+
from urllib.request import urlretrieve
195+
import venv
196+
197+
class DistributeEnvBuilder(venv.EnvBuilder):
198+
"""
199+
This builder installs Distribute and pip so that you can pip or
200+
easy_install other packages into the created environment.
201+
202+
:param nodist: If True, Distribute is not installed into the created
203+
environment.
204+
:param nopip: If True, pip is not installed into the created
205+
environment.
206+
:param progress: If Distribute or pip are installed, the progress of the
207+
installation can be monitored by passing a progress
208+
callable. If specified, it is called with two
209+
arguments: a string indicating some progress, and a
210+
context indicating where the string is coming from.
211+
The context argument can have one of three values:
212+
'main', indicating that it is called from virtualize()
213+
itself, and 'stdout' and 'stderr', which are obtained
214+
by reading lines from the output streams of a subprocess
215+
which is used to install the app.
216+
217+
If a callable is not specified, default progress
218+
information is output to sys.stderr.
219+
"""
220+
221+
def __init__(self, *args, **kwargs):
222+
self.nodist = kwargs.pop('nodist', False)
223+
self.nopip = kwargs.pop('nopip', False)
224+
self.progress = kwargs.pop('progress', None)
225+
self.verbose = kwargs.pop('verbose', False)
226+
super().__init__(*args, **kwargs)
227+
228+
def post_setup(self, context):
229+
"""
230+
Set up any packages which need to be pre-installed into the
231+
environment being created.
232+
233+
:param context: The information for the environment creation request
234+
being processed.
235+
"""
236+
if not self.nodist:
237+
self.install_distribute(context)
238+
if not self.nopip:
239+
self.install_pip(context)
240+
241+
def reader(self, stream, context):
242+
"""
243+
Read lines from a subprocess' output stream and either pass to a progress
244+
callable (if specified) or write progress information to sys.stderr.
245+
"""
246+
progress = self.progress
247+
while True:
248+
s = stream.readline()
249+
if not s:
250+
break
251+
if progress is not None:
252+
progress(s, context)
253+
else:
254+
if not self.verbose:
255+
sys.stderr.write('.')
256+
else:
257+
sys.stderr.write(s.decode('utf-8'))
258+
sys.stderr.flush()
259+
260+
def install_script(self, context, name, url):
261+
_, _, path, _, _, _ = urlparse(url)
262+
fn = os.path.split(path)[-1]
263+
binpath = context.bin_path
264+
distpath = os.path.join(binpath, fn)
265+
# Download script into the env's binaries folder
266+
urlretrieve(url, distpath)
267+
progress = self.progress
268+
if progress is not None:
269+
progress('Installing %s' %name, 'main')
270+
else:
271+
sys.stderr.write('Installing %s ' % name)
272+
sys.stderr.flush()
273+
# Install in the env
274+
args = [context.env_exe, fn]
275+
p = Popen(args, stdout=PIPE, stderr=PIPE, cwd=binpath)
276+
t1 = Thread(target=self.reader, args=(p.stdout, 'stdout'))
277+
t1.start()
278+
t2 = Thread(target=self.reader, args=(p.stderr, 'stderr'))
279+
t2.start()
280+
p.wait()
281+
t1.join()
282+
t2.join()
283+
if progress is not None:
284+
progress('done.', 'main')
285+
else:
286+
sys.stderr.write('done.\n')
287+
# Clean up - no longer needed
288+
os.unlink(distpath)
289+
290+
def install_distribute(self, context):
291+
"""
292+
Install Distribute in the environment.
293+
294+
:param context: The information for the environment creation request
295+
being processed.
296+
"""
297+
url = 'http://python-distribute.org/distribute_setup.py'
298+
self.install_script(context, 'distribute', url)
299+
# clear up the distribute archive which gets downloaded
300+
pred = lambda o: o.startswith('distribute-') and o.endswith('.tar.gz')
301+
files = filter(pred, os.listdir(context.bin_path))
302+
for f in files:
303+
f = os.path.join(context.bin_path, f)
304+
os.unlink(f)
305+
306+
def install_pip(self, context):
307+
"""
308+
Install pip in the environment.
309+
310+
:param context: The information for the environment creation request
311+
being processed.
312+
"""
313+
url = 'https://raw.github.com/pypa/pip/master/contrib/get-pip.py'
314+
self.install_script(context, 'pip', url)
315+
316+
def main(args=None):
317+
compatible = True
318+
if sys.version_info < (3, 3):
319+
compatible = False
320+
elif not hasattr(sys, 'base_prefix'):
321+
compatible = False
322+
if not compatible:
323+
raise ValueError('This script is only for use with '
324+
'Python 3.3 or later')
325+
else:
326+
import argparse
327+
328+
parser = argparse.ArgumentParser(prog=__name__,
329+
description='Creates virtual Python '
330+
'environments in one or '
331+
'more target '
332+
'directories.')
333+
parser.add_argument('dirs', metavar='ENV_DIR', nargs='+',
334+
help='A directory to create the environment in.')
335+
parser.add_argument('--no-distribute', default=False,
336+
action='store_true', dest='nodist',
337+
help="Don't install Distribute in the virtual "
338+
"environment.")
339+
parser.add_argument('--no-pip', default=False,
340+
action='store_true', dest='nopip',
341+
help="Don't install pip in the virtual "
342+
"environment.")
343+
parser.add_argument('--system-site-packages', default=False,
344+
action='store_true', dest='system_site',
345+
help='Give the virtual environment access to the '
346+
'system site-packages dir.')
347+
if os.name == 'nt':
348+
use_symlinks = False
349+
else:
350+
use_symlinks = True
351+
parser.add_argument('--symlinks', default=use_symlinks,
352+
action='store_true', dest='symlinks',
353+
help='Try to use symlinks rather than copies, '
354+
'when symlinks are not the default for '
355+
'the platform.')
356+
parser.add_argument('--clear', default=False, action='store_true',
357+
dest='clear', help='Delete the contents of the '
358+
'environment directory if it '
359+
'already exists, before '
360+
'environment creation.')
361+
parser.add_argument('--upgrade', default=False, action='store_true',
362+
dest='upgrade', help='Upgrade the environment '
363+
'directory to use this version '
364+
'of Python, assuming Python '
365+
'has been upgraded in-place.')
366+
parser.add_argument('--verbose', default=False, action='store_true',
367+
dest='verbose', help='Display the output '
368+
'from the scripts which '
369+
'install Distribute and pip.')
370+
options = parser.parse_args(args)
371+
if options.upgrade and options.clear:
372+
raise ValueError('you cannot supply --upgrade and --clear together.')
373+
builder = DistributeEnvBuilder(system_site_packages=options.system_site,
374+
clear=options.clear,
375+
symlinks=options.symlinks,
376+
upgrade=options.upgrade,
377+
nodist=options.nodist,
378+
nopip=options.nopip,
379+
verbose=options.verbose)
380+
for d in options.dirs:
381+
builder.create(d)
382+
383+
if __name__ == '__main__':
384+
rc = 1
385+
try:
386+
main()
387+
rc = 0
388+
except Exception as e:
389+
print('Error: %s' % e, file=sys.stderr)
390+
sys.exit(rc)
391+
392+
This script is also available for download `online
393+
<https://gist.github.com/4673395>`_.

0 commit comments

Comments
 (0)