summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVinay Sajip <vinay_sajip@yahoo.co.uk>2012-10-11 16:22:45 (GMT)
committerVinay Sajip <vinay_sajip@yahoo.co.uk>2012-10-11 16:22:45 (GMT)
commitbd40d3e1442a49c4f5a2f87c1778a727fafa7bbc (patch)
treeab4126b4c50d9f5793bc87ad56df474a48fd6ca2
parent24dfdb69ca5721d3e0de5686e79ae620abfa6db8 (diff)
downloadcpython-bd40d3e1442a49c4f5a2f87c1778a727fafa7bbc.zip
cpython-bd40d3e1442a49c4f5a2f87c1778a727fafa7bbc.tar.gz
cpython-bd40d3e1442a49c4f5a2f87c1778a727fafa7bbc.tar.bz2
Closes #15776: pyvenv now works with existing directories.
-rw-r--r--Doc/library/venv.rst5
-rw-r--r--Lib/test/test_venv.py59
-rw-r--r--Lib/venv/__init__.py23
3 files changed, 74 insertions, 13 deletions
diff --git a/Doc/library/venv.rst b/Doc/library/venv.rst
index 2499962..b2ecd93 100644
--- a/Doc/library/venv.rst
+++ b/Doc/library/venv.rst
@@ -75,8 +75,8 @@ creation according to their needs, the :class:`EnvBuilder` class.
* ``system_site_packages`` -- a Boolean value indicating that the system Python
site-packages should be available to the environment (defaults to ``False``).
- * ``clear`` -- a Boolean value which, if True, will delete any existing target
- directory instead of raising an exception (defaults to ``False``).
+ * ``clear`` -- a Boolean value which, if True, will delete the contents of
+ any existing target directory, before creating the environment.
* ``symlinks`` -- a Boolean value indicating whether to attempt to symlink the
Python binary (and any necessary DLLs or other binaries,
@@ -88,7 +88,6 @@ creation according to their needs, the :class:`EnvBuilder` class.
upgraded in-place (defaults to ``False``).
-
Creators of third-party virtual environment tools will be free to use the
provided ``EnvBuilder`` class as a base class.
diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py
index 9696844..2a528c9 100644
--- a/Lib/test/test_venv.py
+++ b/Lib/test/test_venv.py
@@ -113,13 +113,68 @@ class BasicTest(BaseTest):
out, err = p.communicate()
self.assertEqual(out.strip(), expected.encode())
+ if sys.platform == 'win32':
+ ENV_SUBDIRS = (
+ ('Scripts',),
+ ('Include',),
+ ('Lib',),
+ ('Lib', 'site-packages'),
+ )
+ else:
+ ENV_SUBDIRS = (
+ ('bin',),
+ ('include',),
+ ('lib',),
+ ('lib', 'python%d.%d' % sys.version_info[:2]),
+ ('lib', 'python%d.%d' % sys.version_info[:2], 'site-packages'),
+ )
+
+ def create_contents(self, paths, filename):
+ """
+ Create some files in the environment which are unrelated
+ to the virtual environment.
+ """
+ for subdirs in paths:
+ d = os.path.join(self.env_dir, *subdirs)
+ os.mkdir(d)
+ fn = os.path.join(d, filename)
+ with open(fn, 'wb') as f:
+ f.write(b'Still here?')
+
def test_overwrite_existing(self):
"""
- Test control of overwriting an existing environment directory.
+ Test creating environment in an existing directory.
"""
- self.assertRaises(ValueError, venv.create, self.env_dir)
+ self.create_contents(self.ENV_SUBDIRS, 'foo')
+ venv.create(self.env_dir)
+ for subdirs in self.ENV_SUBDIRS:
+ fn = os.path.join(self.env_dir, *(subdirs + ('foo',)))
+ self.assertTrue(os.path.exists(fn))
+ with open(fn, 'rb') as f:
+ self.assertEqual(f.read(), b'Still here?')
+
builder = venv.EnvBuilder(clear=True)
builder.create(self.env_dir)
+ for subdirs in self.ENV_SUBDIRS:
+ fn = os.path.join(self.env_dir, *(subdirs + ('foo',)))
+ self.assertFalse(os.path.exists(fn))
+
+ def clear_directory(self, path):
+ for fn in os.listdir(path):
+ fn = os.path.join(path, fn)
+ if os.path.islink(fn) or os.path.isfile(fn):
+ os.remove(fn)
+ elif os.path.isdir(fn):
+ shutil.rmtree(fn)
+
+ def test_unoverwritable_fails(self):
+ #create a file clashing with directories in the env dir
+ for paths in self.ENV_SUBDIRS[:3]:
+ fn = os.path.join(self.env_dir, *paths)
+ with open(fn, 'wb') as f:
+ f.write(b'')
+ self.assertRaises((ValueError, OSError), venv.create, self.env_dir)
+ self.clear_directory(self.env_dir)
def test_upgrade(self):
"""
diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py
index 8d2deb7..e7373e1 100644
--- a/Lib/venv/__init__.py
+++ b/Lib/venv/__init__.py
@@ -90,6 +90,14 @@ class EnvBuilder:
self.setup_scripts(context)
self.post_setup(context)
+ def clear_directory(self, path):
+ for fn in os.listdir(path):
+ fn = os.path.join(path, fn)
+ if os.path.islink(fn) or os.path.isfile(fn):
+ os.remove(fn)
+ elif os.path.isdir(fn):
+ shutil.rmtree(fn)
+
def ensure_directories(self, env_dir):
"""
Create the directories for the environment.
@@ -101,11 +109,11 @@ class EnvBuilder:
def create_if_needed(d):
if not os.path.exists(d):
os.makedirs(d)
+ elif os.path.islink(d) or os.path.isfile(d):
+ raise ValueError('Unable to create directory %r' % d)
- if os.path.exists(env_dir) and not (self.clear or self.upgrade):
- raise ValueError('Directory exists: %s' % env_dir)
if os.path.exists(env_dir) and self.clear:
- shutil.rmtree(env_dir)
+ self.clear_directory(env_dir)
context = Context()
context.env_dir = env_dir
context.env_name = os.path.split(env_dir)[1]
@@ -369,11 +377,10 @@ def main(args=None):
'when symlinks are not the default for '
'the platform.')
parser.add_argument('--clear', default=False, action='store_true',
- dest='clear', help='Delete the environment '
- 'directory if it already '
- 'exists. If not specified and '
- 'the directory exists, an error'
- ' is raised.')
+ dest='clear', help='Delete the contents of the '
+ 'environment directory if it '
+ 'already exists, before '
+ 'environment creation.')
parser.add_argument('--upgrade', default=False, action='store_true',
dest='upgrade', help='Upgrade the environment '
'directory to use this version '