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

Skip to content

Commit bd40d3e

Browse files
committed
Closes #15776: pyvenv now works with existing directories.
1 parent 24dfdb6 commit bd40d3e

3 files changed

Lines changed: 74 additions & 13 deletions

File tree

Doc/library/venv.rst

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ creation according to their needs, the :class:`EnvBuilder` class.
7575
* ``system_site_packages`` -- a Boolean value indicating that the system Python
7676
site-packages should be available to the environment (defaults to ``False``).
7777

78-
* ``clear`` -- a Boolean value which, if True, will delete any existing target
79-
directory instead of raising an exception (defaults to ``False``).
78+
* ``clear`` -- a Boolean value which, if True, will delete the contents of
79+
any existing target directory, before creating the environment.
8080

8181
* ``symlinks`` -- a Boolean value indicating whether to attempt to symlink the
8282
Python binary (and any necessary DLLs or other binaries,
@@ -88,7 +88,6 @@ creation according to their needs, the :class:`EnvBuilder` class.
8888
upgraded in-place (defaults to ``False``).
8989

9090

91-
9291
Creators of third-party virtual environment tools will be free to use the
9392
provided ``EnvBuilder`` class as a base class.
9493

Lib/test/test_venv.py

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,68 @@ def test_prefixes(self):
113113
out, err = p.communicate()
114114
self.assertEqual(out.strip(), expected.encode())
115115

116+
if sys.platform == 'win32':
117+
ENV_SUBDIRS = (
118+
('Scripts',),
119+
('Include',),
120+
('Lib',),
121+
('Lib', 'site-packages'),
122+
)
123+
else:
124+
ENV_SUBDIRS = (
125+
('bin',),
126+
('include',),
127+
('lib',),
128+
('lib', 'python%d.%d' % sys.version_info[:2]),
129+
('lib', 'python%d.%d' % sys.version_info[:2], 'site-packages'),
130+
)
131+
132+
def create_contents(self, paths, filename):
133+
"""
134+
Create some files in the environment which are unrelated
135+
to the virtual environment.
136+
"""
137+
for subdirs in paths:
138+
d = os.path.join(self.env_dir, *subdirs)
139+
os.mkdir(d)
140+
fn = os.path.join(d, filename)
141+
with open(fn, 'wb') as f:
142+
f.write(b'Still here?')
143+
116144
def test_overwrite_existing(self):
117145
"""
118-
Test control of overwriting an existing environment directory.
146+
Test creating environment in an existing directory.
119147
"""
120-
self.assertRaises(ValueError, venv.create, self.env_dir)
148+
self.create_contents(self.ENV_SUBDIRS, 'foo')
149+
venv.create(self.env_dir)
150+
for subdirs in self.ENV_SUBDIRS:
151+
fn = os.path.join(self.env_dir, *(subdirs + ('foo',)))
152+
self.assertTrue(os.path.exists(fn))
153+
with open(fn, 'rb') as f:
154+
self.assertEqual(f.read(), b'Still here?')
155+
121156
builder = venv.EnvBuilder(clear=True)
122157
builder.create(self.env_dir)
158+
for subdirs in self.ENV_SUBDIRS:
159+
fn = os.path.join(self.env_dir, *(subdirs + ('foo',)))
160+
self.assertFalse(os.path.exists(fn))
161+
162+
def clear_directory(self, path):
163+
for fn in os.listdir(path):
164+
fn = os.path.join(path, fn)
165+
if os.path.islink(fn) or os.path.isfile(fn):
166+
os.remove(fn)
167+
elif os.path.isdir(fn):
168+
shutil.rmtree(fn)
169+
170+
def test_unoverwritable_fails(self):
171+
#create a file clashing with directories in the env dir
172+
for paths in self.ENV_SUBDIRS[:3]:
173+
fn = os.path.join(self.env_dir, *paths)
174+
with open(fn, 'wb') as f:
175+
f.write(b'')
176+
self.assertRaises((ValueError, OSError), venv.create, self.env_dir)
177+
self.clear_directory(self.env_dir)
123178

124179
def test_upgrade(self):
125180
"""

Lib/venv/__init__.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ def create(self, env_dir):
9090
self.setup_scripts(context)
9191
self.post_setup(context)
9292

93+
def clear_directory(self, path):
94+
for fn in os.listdir(path):
95+
fn = os.path.join(path, fn)
96+
if os.path.islink(fn) or os.path.isfile(fn):
97+
os.remove(fn)
98+
elif os.path.isdir(fn):
99+
shutil.rmtree(fn)
100+
93101
def ensure_directories(self, env_dir):
94102
"""
95103
Create the directories for the environment.
@@ -101,11 +109,11 @@ def ensure_directories(self, env_dir):
101109
def create_if_needed(d):
102110
if not os.path.exists(d):
103111
os.makedirs(d)
112+
elif os.path.islink(d) or os.path.isfile(d):
113+
raise ValueError('Unable to create directory %r' % d)
104114

105-
if os.path.exists(env_dir) and not (self.clear or self.upgrade):
106-
raise ValueError('Directory exists: %s' % env_dir)
107115
if os.path.exists(env_dir) and self.clear:
108-
shutil.rmtree(env_dir)
116+
self.clear_directory(env_dir)
109117
context = Context()
110118
context.env_dir = env_dir
111119
context.env_name = os.path.split(env_dir)[1]
@@ -369,11 +377,10 @@ def main(args=None):
369377
'when symlinks are not the default for '
370378
'the platform.')
371379
parser.add_argument('--clear', default=False, action='store_true',
372-
dest='clear', help='Delete the environment '
373-
'directory if it already '
374-
'exists. If not specified and '
375-
'the directory exists, an error'
376-
' is raised.')
380+
dest='clear', help='Delete the contents of the '
381+
'environment directory if it '
382+
'already exists, before '
383+
'environment creation.')
377384
parser.add_argument('--upgrade', default=False, action='store_true',
378385
dest='upgrade', help='Upgrade the environment '
379386
'directory to use this version '

0 commit comments

Comments
 (0)