1
|
|
|
# -*- coding: utf-8 -*- |
2
|
|
|
# Copyright (C) 2012 Anaconda, Inc |
3
|
|
|
# SPDX-License-Identifier: BSD-3-Clause |
4
|
|
|
""" |
5
|
|
|
Sections in this module are |
6
|
|
|
|
7
|
|
|
1. top-level functions |
8
|
|
|
2. plan creators |
9
|
|
|
3. plan runners |
10
|
|
|
4. individual operations |
11
|
|
|
5. helper functions |
12
|
|
|
|
13
|
|
|
The top-level functions compose and execute full plans. |
14
|
|
|
|
15
|
|
|
A plan is created by composing various individual operations. The plan data structure is a |
16
|
|
|
list of dicts, where each dict represents an individual operation. The dict contains two |
17
|
|
|
keys--`function` and `kwargs`--where function is the name of the individual operation function |
18
|
|
|
within this module. |
19
|
|
|
|
20
|
|
|
Each individual operation must |
21
|
|
|
|
22
|
|
|
a) return a `Result` (i.e. NEEDS_SUDO, MODIFIED, or NO_CHANGE) |
23
|
|
|
b) have no side effects if context.dry_run is True |
24
|
|
|
c) be verbose and descriptive about the changes being made or proposed is context.verbosity >= 1 |
25
|
|
|
|
26
|
|
|
The plan runner functions take the plan (list of dicts) as an argument, and then coordinate the |
27
|
|
|
execution of each individual operation. The docstring for `run_plan_elevated()` has details on |
28
|
|
|
how that strategy is implemented. |
29
|
|
|
|
30
|
|
|
""" |
31
|
|
|
from __future__ import absolute_import, division, print_function, unicode_literals |
32
|
|
|
|
33
|
|
|
from difflib import unified_diff |
34
|
|
|
from errno import ENOENT |
35
|
|
|
from glob import glob |
36
|
|
|
from itertools import chain |
37
|
|
|
import json |
38
|
|
|
from logging import getLogger |
39
|
|
|
import os |
40
|
|
|
from os.path import abspath, basename, dirname, exists, expanduser, isdir, isfile, join |
41
|
|
|
from random import randint |
42
|
|
|
import re |
43
|
|
|
import sys |
44
|
|
|
from tempfile import NamedTemporaryFile |
45
|
|
|
|
46
|
|
|
from .. import CONDA_PACKAGE_ROOT, CondaError, __version__ as CONDA_VERSION |
47
|
|
|
from .._vendor.auxlib.ish import dals |
48
|
|
|
from ..activate import CshActivator, FishActivator, PosixActivator, XonshActivator |
49
|
|
|
from ..base.context import context |
50
|
|
|
from ..common.compat import (PY2, ensure_binary, ensure_fs_path_encoding, ensure_unicode, on_mac, |
51
|
|
|
on_win, open) |
52
|
|
|
from ..common.path import (expand, get_bin_directory_short_path, get_python_short_path, |
53
|
|
|
get_python_site_packages_short_path, win_path_ok) |
54
|
|
|
from ..exceptions import CondaValueError |
55
|
|
|
from ..gateways.disk.create import copy, mkdir_p |
56
|
|
|
from ..gateways.disk.delete import rm_rf |
57
|
|
|
from ..gateways.disk.link import lexists |
58
|
|
|
from ..gateways.disk.permissions import make_executable |
59
|
|
|
from ..gateways.disk.read import compute_md5sum |
60
|
|
|
from ..gateways.subprocess import subprocess_call |
61
|
|
|
|
62
|
|
|
if on_win: |
63
|
|
|
if PY2: |
64
|
|
|
import _winreg as winreg |
65
|
|
|
else: |
66
|
|
|
import winreg |
67
|
|
|
from menuinst.knownfolders import get_folder_path, FOLDERID |
68
|
|
|
from menuinst.winshortcut import create_shortcut |
69
|
|
|
|
70
|
|
|
|
71
|
|
|
log = getLogger(__name__) |
72
|
|
|
|
73
|
|
|
|
74
|
|
|
class Result: |
75
|
|
|
NEEDS_SUDO = "needs sudo" |
76
|
|
|
MODIFIED = "modified" |
77
|
|
|
NO_CHANGE = "no change" |
78
|
|
|
|
79
|
|
|
|
80
|
|
|
# ##################################################### |
81
|
|
|
# top-level functions |
82
|
|
|
# ##################################################### |
83
|
|
|
|
84
|
|
|
def install(conda_prefix): |
85
|
|
|
plan = make_install_plan(conda_prefix) |
86
|
|
|
run_plan(plan) |
87
|
|
|
if not context.dry_run: |
88
|
|
|
assert not any(step['result'] == Result.NEEDS_SUDO for step in plan) |
89
|
|
|
print_plan_results(plan) |
90
|
|
|
|
91
|
|
|
|
92
|
|
|
def initialize(conda_prefix, shells, for_user, for_system, anaconda_prompt): |
93
|
|
|
plan1 = [] |
94
|
|
|
if os.getenv('CONDA_PIP_UNINITIALIZED') == 'true': |
95
|
|
|
plan1 = make_install_plan(conda_prefix) |
96
|
|
|
run_plan(plan1) |
97
|
|
|
if not context.dry_run: |
98
|
|
|
run_plan_elevated(plan1) |
99
|
|
|
|
100
|
|
|
plan2 = make_initialize_plan(conda_prefix, shells, for_user, for_system, anaconda_prompt) |
101
|
|
|
run_plan(plan2) |
102
|
|
|
if not context.dry_run: |
103
|
|
|
run_plan_elevated(plan2) |
104
|
|
|
|
105
|
|
|
plan = plan1 + plan2 |
106
|
|
|
print_plan_results(plan) |
107
|
|
|
|
108
|
|
|
if any(step['result'] == Result.NEEDS_SUDO for step in plan): |
109
|
|
|
print("Operation failed.", file=sys.stderr) |
110
|
|
|
return 1 |
111
|
|
|
|
112
|
|
|
|
113
|
|
|
def initialize_dev(shell, dev_env_prefix=None, conda_source_root=None): |
114
|
|
|
# > alias conda-dev='eval "$(python -m conda init --dev)"' |
115
|
|
|
# > eval "$(python -m conda init --dev)" |
116
|
|
|
|
117
|
|
|
dev_env_prefix = expand(dev_env_prefix or sys.prefix) |
118
|
|
|
conda_source_root = expand(conda_source_root or os.getcwd()) |
119
|
|
|
|
120
|
|
|
python_exe, python_version, site_packages_dir = _get_python_info(dev_env_prefix) |
121
|
|
|
|
122
|
|
|
if not isfile(join(conda_source_root, 'conda', '__main__.py')): |
123
|
|
|
raise CondaValueError("Directory is not a conda source root: %s" % conda_source_root) |
124
|
|
|
|
125
|
|
|
plan = make_install_plan(dev_env_prefix) |
126
|
|
|
plan.append({ |
127
|
|
|
'function': remove_conda_in_sp_dir.__name__, |
128
|
|
|
'kwargs': { |
129
|
|
|
'target_path': site_packages_dir, |
130
|
|
|
}, |
131
|
|
|
}) |
132
|
|
|
plan.append({ |
133
|
|
|
'function': make_conda_egg_link.__name__, |
134
|
|
|
'kwargs': { |
135
|
|
|
'target_path': join(site_packages_dir, 'conda.egg-link'), |
136
|
|
|
'conda_source_root': conda_source_root, |
137
|
|
|
}, |
138
|
|
|
}) |
139
|
|
|
plan.append({ |
140
|
|
|
'function': modify_easy_install_pth.__name__, |
141
|
|
|
'kwargs': { |
142
|
|
|
'target_path': join(site_packages_dir, 'easy-install.pth'), |
143
|
|
|
'conda_source_root': conda_source_root, |
144
|
|
|
}, |
145
|
|
|
}) |
146
|
|
|
plan.append({ |
147
|
|
|
'function': make_dev_egg_info_file.__name__, |
148
|
|
|
'kwargs': { |
149
|
|
|
'target_path': join(conda_source_root, 'conda.egg-info'), |
150
|
|
|
}, |
151
|
|
|
}) |
152
|
|
|
|
153
|
|
|
run_plan(plan) |
154
|
|
|
|
155
|
|
|
if context.dry_run or context.verbosity: |
156
|
|
|
print_plan_results(plan, sys.stderr) |
157
|
|
|
|
158
|
|
|
if any(step['result'] == Result.NEEDS_SUDO for step in plan): # pragma: no cover |
159
|
|
|
raise CondaError("Operation failed. Privileged install disallowed for 'conda init --dev'.") |
160
|
|
|
|
161
|
|
|
env_vars = { |
162
|
|
|
'ADD_COV': '--cov-report xml --cov-report term-missing --cov conda', |
163
|
|
|
'PYTHONHASHSEED': str(randint(0, 4294967296)), |
164
|
|
|
'PYTHON_MAJOR_VERSION': python_version[0], |
165
|
|
|
'TEST_PLATFORM': 'win' if on_win else 'unix', |
166
|
|
|
} |
167
|
|
|
unset_env_vars = ( |
168
|
|
|
'CONDA_DEFAULT_ENV', |
169
|
|
|
'CONDA_EXE', |
170
|
|
|
'CONDA_PREFIX', |
171
|
|
|
'CONDA_PREFIX_1', |
172
|
|
|
'CONDA_PREFIX_2', |
173
|
|
|
'CONDA_PROMPT_MODIFIER', |
174
|
|
|
'CONDA_PYTHON_EXE', |
175
|
|
|
'CONDA_SHLVL', |
176
|
|
|
) |
177
|
|
|
|
178
|
|
|
if shell == "bash": |
179
|
|
|
builder = [] |
180
|
|
|
builder += ["unset %s" % unset_env_var for unset_env_var in unset_env_vars] |
181
|
|
|
builder += ["export %s='%s'" % (key, env_vars[key]) for key in sorted(env_vars)] |
182
|
|
|
sys_executable = abspath(sys.executable) |
183
|
|
|
if on_win: |
184
|
|
|
sys_executable = "$(cygpath '%s')" % sys_executable |
185
|
|
|
builder += [ |
186
|
|
|
"eval \"$(\"%s\" -m conda shell.bash hook)\"" % sys_executable, |
187
|
|
|
"conda activate '%s'" % dev_env_prefix, |
188
|
|
|
] |
189
|
|
|
print("\n".join(builder)) |
190
|
|
|
elif shell == 'cmd.exe': |
191
|
|
|
builder = [] |
192
|
|
|
builder += ["@IF NOT \"%CONDA_PROMPT_MODIFIER%\" == \"\" @CALL " |
193
|
|
|
"SET \"PROMPT=%%PROMPT:%CONDA_PROMPT_MODIFIER%=%_empty_not_set_%%%\""] |
194
|
|
|
builder += ["@SET %s=" % unset_env_var for unset_env_var in unset_env_vars] |
195
|
|
|
builder += ['@SET "%s=%s"' % (key, env_vars[key]) for key in sorted(env_vars)] |
196
|
|
|
builder += [ |
197
|
|
|
'@CALL \"%s\"' % join(dev_env_prefix, 'condacmd', 'conda_hook.bat'), |
198
|
|
|
'@IF %errorlevel% NEQ 0 @EXIT /B %errorlevel%', |
199
|
|
|
'@CALL \"%s\" activate \"%s\"' % (join(dev_env_prefix, 'condacmd', 'conda.bat'), |
200
|
|
|
dev_env_prefix), |
201
|
|
|
'@IF %errorlevel% NEQ 0 @EXIT /B %errorlevel%', |
202
|
|
|
] |
203
|
|
|
if not context.dry_run: |
204
|
|
|
with open('dev-init.bat', 'w') as fh: |
205
|
|
|
fh.write('\n'.join(builder)) |
206
|
|
|
if context.verbosity: |
207
|
|
|
print('\n'.join(builder)) |
208
|
|
|
print("now run > .\\dev-init.bat") |
209
|
|
|
else: |
210
|
|
|
raise NotImplementedError() |
211
|
|
|
|
212
|
|
|
|
213
|
|
|
# ##################################################### |
214
|
|
|
# plan creators |
215
|
|
|
# ##################################################### |
216
|
|
|
|
217
|
|
|
def make_install_plan(conda_prefix): |
218
|
|
|
try: |
219
|
|
|
python_exe, python_version, site_packages_dir = _get_python_info(conda_prefix) |
220
|
|
|
except EnvironmentError: |
221
|
|
|
python_exe, python_version, site_packages_dir = None, None, None # NOQA |
222
|
|
|
|
223
|
|
|
plan = [] |
224
|
|
|
|
225
|
|
|
# ###################################### |
226
|
|
|
# executables |
227
|
|
|
# ###################################### |
228
|
|
|
if on_win: |
229
|
|
|
conda_exe_path = join(conda_prefix, 'Scripts', 'conda-script.py') |
230
|
|
|
conda_env_exe_path = join(conda_prefix, 'Scripts', 'conda-env-script.py') |
231
|
|
|
plan.append({ |
232
|
|
|
'function': make_entry_point_exe.__name__, |
233
|
|
|
'kwargs': { |
234
|
|
|
'target_path': join(conda_prefix, 'Scripts', 'conda.exe'), |
235
|
|
|
'conda_prefix': conda_prefix, |
236
|
|
|
}, |
237
|
|
|
}) |
238
|
|
|
plan.append({ |
239
|
|
|
'function': make_entry_point_exe.__name__, |
240
|
|
|
'kwargs': { |
241
|
|
|
'target_path': join(conda_prefix, 'Scripts', 'conda-env.exe'), |
242
|
|
|
'conda_prefix': conda_prefix, |
243
|
|
|
}, |
244
|
|
|
}) |
245
|
|
|
else: |
246
|
|
|
conda_exe_path = join(conda_prefix, 'bin', 'conda') |
247
|
|
|
conda_env_exe_path = join(conda_prefix, 'bin', 'conda-env') |
248
|
|
|
|
249
|
|
|
plan.append({ |
250
|
|
|
'function': make_entry_point.__name__, |
251
|
|
|
'kwargs': { |
252
|
|
|
'target_path': conda_exe_path, |
253
|
|
|
'conda_prefix': conda_prefix, |
254
|
|
|
'module': 'conda.cli', |
255
|
|
|
'func': 'main', |
256
|
|
|
}, |
257
|
|
|
}) |
258
|
|
|
plan.append({ |
259
|
|
|
'function': make_entry_point.__name__, |
260
|
|
|
'kwargs': { |
261
|
|
|
'target_path': conda_env_exe_path, |
262
|
|
|
'conda_prefix': conda_prefix, |
263
|
|
|
'module': 'conda_env.cli.main', |
264
|
|
|
'func': 'main', |
265
|
|
|
}, |
266
|
|
|
}) |
267
|
|
|
|
268
|
|
|
# ###################################### |
269
|
|
|
# shell wrappers |
270
|
|
|
# ###################################### |
271
|
|
|
if on_win: |
272
|
|
|
plan.append({ |
273
|
|
|
'function': install_condacmd_conda_bat.__name__, |
274
|
|
|
'kwargs': { |
275
|
|
|
'target_path': join(conda_prefix, 'condacmd', 'conda.bat'), |
276
|
|
|
'conda_prefix': conda_prefix, |
277
|
|
|
}, |
278
|
|
|
}) |
279
|
|
|
plan.append({ |
280
|
|
|
'function': install_condacmd_conda_activate_bat.__name__, |
281
|
|
|
'kwargs': { |
282
|
|
|
'target_path': join(conda_prefix, 'condacmd', '_conda_activate.bat'), |
283
|
|
|
'conda_prefix': conda_prefix, |
284
|
|
|
}, |
285
|
|
|
}) |
286
|
|
|
plan.append({ |
287
|
|
|
'function': install_condacmd_conda_auto_activate_bat.__name__, |
288
|
|
|
'kwargs': { |
289
|
|
|
'target_path': join(conda_prefix, 'condacmd', 'conda_auto_activate.bat'), |
290
|
|
|
'conda_prefix': conda_prefix, |
291
|
|
|
}, |
292
|
|
|
}) |
293
|
|
|
plan.append({ |
294
|
|
|
'function': install_condacmd_hook_bat.__name__, |
295
|
|
|
'kwargs': { |
296
|
|
|
'target_path': join(conda_prefix, 'condacmd', 'conda_hook.bat'), |
297
|
|
|
'conda_prefix': conda_prefix, |
298
|
|
|
}, |
299
|
|
|
}) |
300
|
|
|
plan.append({ |
301
|
|
|
'function': install_Scripts_activate_bat.__name__, |
302
|
|
|
'kwargs': { |
303
|
|
|
'target_path': join(conda_prefix, 'Scripts', 'activate.bat'), |
304
|
|
|
'conda_prefix': conda_prefix, |
305
|
|
|
}, |
306
|
|
|
}) |
307
|
|
|
plan.append({ |
308
|
|
|
'function': install_activate_bat.__name__, |
309
|
|
|
'kwargs': { |
310
|
|
|
'target_path': join(conda_prefix, 'condacmd', 'activate.bat'), |
311
|
|
|
'conda_prefix': conda_prefix, |
312
|
|
|
}, |
313
|
|
|
}) |
314
|
|
|
plan.append({ |
315
|
|
|
'function': install_deactivate_bat.__name__, |
316
|
|
|
'kwargs': { |
317
|
|
|
'target_path': join(conda_prefix, 'condacmd', 'deactivate.bat'), |
318
|
|
|
'conda_prefix': conda_prefix, |
319
|
|
|
}, |
320
|
|
|
}) |
321
|
|
|
|
322
|
|
|
plan.append({ |
323
|
|
|
'function': install_activate.__name__, |
324
|
|
|
'kwargs': { |
325
|
|
|
'target_path': join(conda_prefix, get_bin_directory_short_path(), 'activate'), |
326
|
|
|
'conda_prefix': conda_prefix, |
327
|
|
|
}, |
328
|
|
|
}) |
329
|
|
|
plan.append({ |
330
|
|
|
'function': install_deactivate.__name__, |
331
|
|
|
'kwargs': { |
332
|
|
|
'target_path': join(conda_prefix, get_bin_directory_short_path(), 'deactivate'), |
333
|
|
|
'conda_prefix': conda_prefix, |
334
|
|
|
}, |
335
|
|
|
}) |
336
|
|
|
|
337
|
|
|
plan.append({ |
338
|
|
|
'function': install_conda_sh.__name__, |
339
|
|
|
'kwargs': { |
340
|
|
|
'target_path': join(conda_prefix, 'etc', 'profile.d', 'conda.sh'), |
341
|
|
|
'conda_prefix': conda_prefix, |
342
|
|
|
}, |
343
|
|
|
}) |
344
|
|
|
plan.append({ |
345
|
|
|
'function': install_conda_fish.__name__, |
346
|
|
|
'kwargs': { |
347
|
|
|
'target_path': join(conda_prefix, 'etc', 'fish', 'conf.d', 'conda.fish'), |
348
|
|
|
'conda_prefix': conda_prefix, |
349
|
|
|
}, |
350
|
|
|
}) |
351
|
|
|
if site_packages_dir: |
352
|
|
|
plan.append({ |
353
|
|
|
'function': install_conda_xsh.__name__, |
354
|
|
|
'kwargs': { |
355
|
|
|
'target_path': join(site_packages_dir, 'xonsh', 'conda.xsh'), |
356
|
|
|
'conda_prefix': conda_prefix, |
357
|
|
|
}, |
358
|
|
|
}) |
359
|
|
|
else: |
360
|
|
|
print("WARNING: Cannot install xonsh wrapper without a python interpreter in prefix: " |
361
|
|
|
"%s" % conda_prefix, file=sys.stderr) |
362
|
|
|
plan.append({ |
363
|
|
|
'function': install_conda_csh.__name__, |
364
|
|
|
'kwargs': { |
365
|
|
|
'target_path': join(conda_prefix, 'etc', 'profile.d', 'conda.csh'), |
366
|
|
|
'conda_prefix': conda_prefix, |
367
|
|
|
}, |
368
|
|
|
}) |
369
|
|
|
return plan |
370
|
|
|
|
371
|
|
|
|
372
|
|
|
def make_initialize_plan(conda_prefix, shells, for_user, for_system, anaconda_prompt): |
373
|
|
|
plan = make_install_plan(conda_prefix) |
374
|
|
|
shells = set(shells) |
375
|
|
|
if shells & {'bash', 'zsh'}: |
376
|
|
|
if 'bash' in shells and for_user: |
377
|
|
|
bashrc_path = expand(join('~', '.bash_profile' if on_mac else '.bashrc')) |
378
|
|
|
plan.append({ |
379
|
|
|
'function': init_sh_user.__name__, |
380
|
|
|
'kwargs': { |
381
|
|
|
'target_path': bashrc_path, |
382
|
|
|
'conda_prefix': conda_prefix, |
383
|
|
|
'shell': 'bash', |
384
|
|
|
}, |
385
|
|
|
}) |
386
|
|
|
|
387
|
|
|
if 'zsh' in shells and for_user: |
388
|
|
|
zshrc_path = expand(join('~', '.zshrc')) |
389
|
|
|
plan.append({ |
390
|
|
|
'function': init_sh_user.__name__, |
391
|
|
|
'kwargs': { |
392
|
|
|
'target_path': zshrc_path, |
393
|
|
|
'conda_prefix': conda_prefix, |
394
|
|
|
'shell': 'zsh', |
395
|
|
|
}, |
396
|
|
|
}) |
397
|
|
|
|
398
|
|
|
if for_system: |
399
|
|
|
plan.append({ |
400
|
|
|
'function': init_sh_system.__name__, |
401
|
|
|
'kwargs': { |
402
|
|
|
'target_path': '/etc/profile.d/conda.sh', |
403
|
|
|
'conda_prefix': conda_prefix, |
404
|
|
|
}, |
405
|
|
|
}) |
406
|
|
|
|
407
|
|
|
if 'fish' in shells: |
408
|
|
|
if for_user: |
409
|
|
|
config_fish_path = expand(join('~', '.config', 'config.fish')) |
410
|
|
|
plan.append({ |
411
|
|
|
'function': init_fish_user.__name__, |
412
|
|
|
'kwargs': { |
413
|
|
|
'target_path': config_fish_path, |
414
|
|
|
'conda_prefix': conda_prefix, |
415
|
|
|
}, |
416
|
|
|
}) |
417
|
|
|
if for_system: |
418
|
|
|
raise NotImplementedError() |
419
|
|
|
|
420
|
|
|
if 'tcsh' in shells: |
421
|
|
|
if for_user: |
422
|
|
|
raise NotImplementedError() |
423
|
|
|
if for_system: |
424
|
|
|
raise NotImplementedError() |
425
|
|
|
|
426
|
|
|
if 'powershell' in shells: |
427
|
|
|
if for_user: |
428
|
|
|
raise NotImplementedError() |
429
|
|
|
if for_system: |
430
|
|
|
raise NotImplementedError() |
431
|
|
|
|
432
|
|
|
if 'cmd.exe' in shells: |
433
|
|
|
if for_user: |
434
|
|
|
plan.append({ |
435
|
|
|
'function': init_cmd_exe_registry.__name__, |
436
|
|
|
'kwargs': { |
437
|
|
|
'target_path': 'HKEY_CURRENT_USER\\Software\\Microsoft\\' |
438
|
|
|
'Command Processor\\AutoRun', |
439
|
|
|
'conda_prefix': conda_prefix, |
440
|
|
|
}, |
441
|
|
|
}) |
442
|
|
|
if for_system: |
443
|
|
|
plan.append({ |
444
|
|
|
'function': init_cmd_exe_registry.__name__, |
445
|
|
|
'kwargs': { |
446
|
|
|
'target_path': 'HKEY_LOCAL_MACHINE\\Software\\Microsoft\\' |
447
|
|
|
'Command Processor\\AutoRun', |
448
|
|
|
'conda_prefix': conda_prefix, |
449
|
|
|
}, |
450
|
|
|
}) |
451
|
|
|
if anaconda_prompt: |
452
|
|
|
plan.append({ |
453
|
|
|
'function': install_anaconda_prompt.__name__, |
454
|
|
|
'kwargs': { |
455
|
|
|
'target_path': join(conda_prefix, 'condacmd', 'Anaconda Prompt.lnk'), |
456
|
|
|
'conda_prefix': conda_prefix, |
457
|
|
|
}, |
458
|
|
|
}) |
459
|
|
|
if on_win: |
460
|
|
|
desktop_dir, exception = get_folder_path(FOLDERID.Desktop) |
461
|
|
|
assert not exception |
462
|
|
|
else: |
463
|
|
|
desktop_dir = join(expanduser('~'), "Desktop") |
464
|
|
|
plan.append({ |
465
|
|
|
'function': install_anaconda_prompt.__name__, |
466
|
|
|
'kwargs': { |
467
|
|
|
'target_path': join(desktop_dir, "Anaconda Prompt.lnk"), |
468
|
|
|
'conda_prefix': conda_prefix, |
469
|
|
|
}, |
470
|
|
|
}) |
471
|
|
|
|
472
|
|
|
return plan |
473
|
|
|
|
474
|
|
|
|
475
|
|
|
# ##################################################### |
476
|
|
|
# plan runners |
477
|
|
|
# ##################################################### |
478
|
|
|
|
479
|
|
|
def run_plan(plan): |
480
|
|
|
for step in plan: |
481
|
|
|
previous_result = step.get('result', None) |
482
|
|
|
if previous_result in (Result.MODIFIED, Result.NO_CHANGE): |
483
|
|
|
continue |
484
|
|
|
try: |
485
|
|
|
result = globals()[step['function']](*step.get('args', ()), **step.get('kwargs', {})) |
486
|
|
|
except EnvironmentError as e: |
487
|
|
|
log.info("%s: %r", step['function'], e, exc_info=True) |
488
|
|
|
result = Result.NEEDS_SUDO |
489
|
|
|
step['result'] = result |
490
|
|
|
|
491
|
|
|
|
492
|
|
|
def run_plan_elevated(plan): |
493
|
|
|
""" |
494
|
|
|
The strategy of this function differs between unix and Windows. Both strategies use a |
495
|
|
|
subprocess call, where the subprocess is run with elevated privileges. The executable |
496
|
|
|
invoked with the subprocess is `python -m conda.core.initialize`, so see the |
497
|
|
|
`if __name__ == "__main__"` at the bottom of this module. |
498
|
|
|
|
499
|
|
|
For unix platforms, we convert the plan list to json, and then call this module with |
500
|
|
|
`sudo python -m conda.core.initialize` while piping the plan json to stdin. We collect json |
501
|
|
|
from stdout for the results of the plan execution with elevated privileges. |
502
|
|
|
|
503
|
|
|
For Windows, we create a temporary file that holds the json content of the plan. The |
504
|
|
|
subprocess reads the content of the file, modifies the content of the file with updated |
505
|
|
|
execution status, and then closes the file. This process then reads the content of that file |
506
|
|
|
for the individual operation execution results, and then deletes the file. |
507
|
|
|
|
508
|
|
|
""" |
509
|
|
|
|
510
|
|
|
if any(step['result'] == Result.NEEDS_SUDO for step in plan): |
511
|
|
|
if on_win: |
512
|
|
|
from menuinst.win_elevate import runAsAdmin |
513
|
|
|
# https://github.com/ContinuumIO/menuinst/blob/master/menuinst/windows/win_elevate.py # no stdin / stdout / stderr pipe support # NOQA |
514
|
|
|
# https://github.com/saltstack/salt-windows-install/blob/master/deps/salt/python/App/Lib/site-packages/win32/Demos/pipes/runproc.py # NOQA |
515
|
|
|
# https://github.com/twonds/twisted/blob/master/twisted/internet/_dumbwin32proc.py |
516
|
|
|
# https://stackoverflow.com/a/19982092/2127762 |
517
|
|
|
# https://www.codeproject.com/Articles/19165/Vista-UAC-The-Definitive-Guide |
518
|
|
|
|
519
|
|
|
# from menuinst.win_elevate import isUserAdmin, runAsAdmin |
520
|
|
|
# I do think we can pipe to stdin, so we're going to have to write to a temp file and read in the elevated process # NOQA |
521
|
|
|
|
522
|
|
|
temp_path = None |
523
|
|
|
try: |
524
|
|
|
with NamedTemporaryFile('w+b', suffix='.json', delete=False) as tf: |
525
|
|
|
# the default mode is 'w+b', and universal new lines don't work in that mode |
526
|
|
|
tf.write(ensure_binary(json.dumps(plan, ensure_ascii=False))) |
527
|
|
|
temp_path = tf.name |
528
|
|
|
rc = runAsAdmin((sys.executable, '-m', 'conda.initialize', '"%s"' % temp_path)) |
529
|
|
|
assert rc == 0 |
530
|
|
|
|
531
|
|
|
with open(temp_path) as fh: |
532
|
|
|
_plan = json.loads(ensure_unicode(fh.read())) |
533
|
|
|
finally: |
534
|
|
|
if temp_path and lexists(temp_path): |
535
|
|
|
rm_rf(temp_path) |
536
|
|
|
|
537
|
|
|
else: |
538
|
|
|
stdin = json.dumps(plan) |
539
|
|
|
result = subprocess_call( |
540
|
|
|
'sudo %s -m conda.initialize' % sys.executable, |
541
|
|
|
env={}, |
542
|
|
|
path=os.getcwd(), |
543
|
|
|
stdin=stdin |
544
|
|
|
) |
545
|
|
|
stderr = result.stderr.strip() |
546
|
|
|
if stderr: |
547
|
|
|
print(stderr, file=sys.stderr) |
548
|
|
|
_plan = json.loads(result.stdout.strip()) |
549
|
|
|
|
550
|
|
|
del plan[:] |
551
|
|
|
plan.extend(_plan) |
552
|
|
|
|
553
|
|
|
|
554
|
|
|
def run_plan_from_stdin(): |
555
|
|
|
stdin = sys.stdin.read().strip() |
556
|
|
|
plan = json.loads(stdin) |
557
|
|
|
run_plan(plan) |
558
|
|
|
sys.stdout.write(json.dumps(plan)) |
559
|
|
|
|
560
|
|
|
|
561
|
|
|
def run_plan_from_temp_file(temp_path): |
562
|
|
|
with open(temp_path) as fh: |
563
|
|
|
plan = json.loads(ensure_unicode(fh.read())) |
564
|
|
|
run_plan(plan) |
565
|
|
|
with open(temp_path, 'w+b') as fh: |
566
|
|
|
fh.write(ensure_binary(json.dumps(plan, ensure_ascii=False))) |
567
|
|
|
|
568
|
|
|
|
569
|
|
|
def print_plan_results(plan, stream=None): |
570
|
|
|
if not stream: |
571
|
|
|
stream = sys.stdout |
572
|
|
|
for step in plan: |
573
|
|
|
print("%-14s%s" % (step.get('result'), step['kwargs']['target_path']), file=stream) |
574
|
|
|
|
575
|
|
|
changed = any(step.get('result') == Result.MODIFIED for step in plan) |
576
|
|
|
if changed: |
577
|
|
|
print("\n==> For changes to take effect, close and re-open your current shell. <==\n", |
578
|
|
|
file=stream) |
579
|
|
|
else: |
580
|
|
|
print("No action taken.", file=stream) |
581
|
|
|
|
582
|
|
|
|
583
|
|
|
# ##################################################### |
584
|
|
|
# individual operations |
585
|
|
|
# ##################################################### |
586
|
|
|
|
587
|
|
|
def make_entry_point(target_path, conda_prefix, module, func): |
588
|
|
|
# target_path: join(conda_prefix, 'bin', 'conda') |
589
|
|
|
conda_ep_path = target_path |
590
|
|
|
|
591
|
|
|
if isfile(conda_ep_path): |
592
|
|
|
with open(conda_ep_path) as fh: |
593
|
|
|
original_ep_content = fh.read() |
594
|
|
|
else: |
595
|
|
|
original_ep_content = "" |
596
|
|
|
|
597
|
|
|
if on_win: |
598
|
|
|
# no shebang needed on windows |
599
|
|
|
new_ep_content = "" |
600
|
|
|
else: |
601
|
|
|
new_ep_content = "#!%s\n" % join(conda_prefix, get_python_short_path()) |
602
|
|
|
|
603
|
|
|
conda_extra = dals(""" |
604
|
|
|
# Before any more imports, leave cwd out of sys.path for internal 'conda shell.*' commands. |
605
|
|
|
# see https://github.com/conda/conda/issues/6549 |
606
|
|
|
if len(sys.argv) > 1 and sys.argv[1].startswith('shell.') and sys.path and sys.path[0] == '': |
607
|
|
|
# The standard first entry in sys.path is an empty string, |
608
|
|
|
# and os.path.abspath('') expands to os.getcwd(). |
609
|
|
|
del sys.path[0] |
610
|
|
|
""") |
611
|
|
|
|
612
|
|
|
new_ep_content += dals(""" |
613
|
|
|
# -*- coding: utf-8 -*- |
614
|
|
|
import sys |
615
|
|
|
%(extra)s |
616
|
|
|
if __name__ == '__main__': |
617
|
|
|
from %(module)s import %(func)s |
618
|
|
|
sys.exit(%(func)s()) |
619
|
|
|
""") % { |
620
|
|
|
'extra': conda_extra if module == 'conda.cli' else '', |
621
|
|
|
'module': module, |
622
|
|
|
'func': func, |
623
|
|
|
} |
624
|
|
|
|
625
|
|
View Code Duplication |
if new_ep_content != original_ep_content: |
|
|
|
|
626
|
|
|
if context.verbosity: |
627
|
|
|
print('\n') |
628
|
|
|
print(target_path) |
629
|
|
|
print(make_diff(original_ep_content, new_ep_content)) |
630
|
|
|
if not context.dry_run: |
631
|
|
|
mkdir_p(dirname(conda_ep_path)) |
632
|
|
|
with open(conda_ep_path, 'w') as fdst: |
633
|
|
|
fdst.write(new_ep_content) |
634
|
|
|
if not on_win: |
635
|
|
|
make_executable(conda_ep_path) |
636
|
|
|
return Result.MODIFIED |
637
|
|
|
else: |
638
|
|
|
return Result.NO_CHANGE |
639
|
|
View Code Duplication |
|
|
|
|
|
640
|
|
|
|
641
|
|
|
def make_entry_point_exe(target_path, conda_prefix): |
642
|
|
|
# target_path: join(conda_prefix, 'Scripts', 'conda.exe') |
643
|
|
|
exe_path = target_path |
644
|
|
|
bits = 8 * tuple.__itemsize__ |
645
|
|
|
source_exe_path = join(CONDA_PACKAGE_ROOT, 'shell', 'cli-%d.exe' % bits) |
646
|
|
|
if isfile(exe_path): |
647
|
|
|
if compute_md5sum(exe_path) == compute_md5sum(source_exe_path): |
648
|
|
|
return Result.NO_CHANGE |
649
|
|
|
|
650
|
|
|
if not context.dry_run: |
651
|
|
|
if not isdir(dirname(exe_path)): |
652
|
|
|
mkdir_p(dirname(exe_path)) |
653
|
|
|
# prefer copy() over create_hard_link_or_copy() because of windows file deletion issues |
654
|
|
|
# with open processes |
655
|
|
|
copy(source_exe_path, exe_path) |
656
|
|
|
return Result.MODIFIED |
657
|
|
|
|
658
|
|
|
|
659
|
|
|
def install_anaconda_prompt(target_path, conda_prefix): |
660
|
|
|
# target_path: join(conda_prefix, 'condacmd', 'Anaconda Prompt.lnk') |
661
|
|
|
# target: join(os.environ["HOMEPATH"], "Desktop", "Anaconda Prompt.lnk") |
662
|
|
|
icon_path = join(CONDA_PACKAGE_ROOT, 'shell', 'conda_icon.ico') |
663
|
|
|
|
664
|
|
|
args = ( |
665
|
|
|
'/K', |
666
|
|
|
'""%s" && "%s""' % (join(conda_prefix, 'condacmd', 'conda_hook.bat'), |
667
|
|
|
join(conda_prefix, 'condacmd', 'conda_auto_activate.bat')), |
668
|
|
|
) |
669
|
|
|
# The API for the call to 'create_shortcut' has 3 |
670
|
|
|
# required arguments (path, description, filename) |
671
|
|
|
# and 4 optional ones (args, working_dir, icon_path, icon_index). |
672
|
|
|
if not context.dry_run: |
673
|
|
|
create_shortcut( |
674
|
|
|
"%windir%\\System32\\cmd.exe", |
675
|
|
|
"Anconda Prompt", |
676
|
|
|
'' + target_path, |
677
|
|
|
' '.join(args), |
678
|
|
|
'' + expanduser('~'), |
679
|
|
|
'' + icon_path, |
680
|
|
|
) |
681
|
|
|
# TODO: need to make idempotent / support NO_CHANGE |
682
|
|
|
return Result.MODIFIED |
683
|
|
|
|
684
|
|
|
|
685
|
|
|
def _install_file(target_path, file_content): |
686
|
|
|
if isfile(target_path): |
687
|
|
|
with open(target_path) as fh: |
688
|
|
|
original_content = fh.read() |
689
|
|
|
else: |
690
|
|
|
original_content = "" |
691
|
|
|
|
692
|
|
|
new_content = file_content |
693
|
|
|
|
694
|
|
|
if new_content != original_content: |
695
|
|
|
if context.verbosity: |
696
|
|
|
print('\n') |
697
|
|
|
print(target_path) |
698
|
|
|
print(make_diff(original_content, new_content)) |
699
|
|
|
if not context.dry_run: |
700
|
|
|
mkdir_p(dirname(target_path)) |
701
|
|
|
with open(target_path, 'w') as fdst: |
702
|
|
|
fdst.write(new_content) |
703
|
|
|
return Result.MODIFIED |
704
|
|
|
else: |
705
|
|
|
return Result.NO_CHANGE |
706
|
|
|
|
707
|
|
|
|
708
|
|
|
def install_conda_sh(target_path, conda_prefix): |
709
|
|
|
# target_path: join(conda_prefix, 'etc', 'profile.d', 'conda.sh') |
710
|
|
|
file_content = PosixActivator().hook(auto_activate_base=False) |
711
|
|
|
return _install_file(target_path, file_content) |
712
|
|
|
|
713
|
|
|
|
714
|
|
|
def install_Scripts_activate_bat(target_path, conda_prefix): |
715
|
|
|
# target_path: join(conda_prefix, 'Scripts', 'activate.bat') |
716
|
|
|
src_path = join(CONDA_PACKAGE_ROOT, 'shell', 'Scripts', 'activate.bat') |
717
|
|
|
with open(src_path) as fsrc: |
718
|
|
|
file_content = fsrc.read() |
719
|
|
|
return _install_file(target_path, file_content) |
720
|
|
|
|
721
|
|
|
|
722
|
|
|
def install_activate_bat(target_path, conda_prefix): |
723
|
|
|
# target_path: join(conda_prefix, 'condacmd', 'activate.bat') |
724
|
|
|
src_path = join(CONDA_PACKAGE_ROOT, 'shell', 'condacmd', 'activate.bat') |
725
|
|
|
with open(src_path) as fsrc: |
726
|
|
|
file_content = fsrc.read() |
727
|
|
|
return _install_file(target_path, file_content) |
728
|
|
|
|
729
|
|
|
|
730
|
|
|
def install_deactivate_bat(target_path, conda_prefix): |
731
|
|
|
# target_path: join(conda_prefix, 'condacmd', 'deactivate.bat') |
732
|
|
|
src_path = join(CONDA_PACKAGE_ROOT, 'shell', 'condacmd', 'deactivate.bat') |
733
|
|
|
with open(src_path) as fsrc: |
734
|
|
|
file_content = fsrc.read() |
735
|
|
|
return _install_file(target_path, file_content) |
736
|
|
|
|
737
|
|
|
|
738
|
|
|
def install_activate(target_path, conda_prefix): |
739
|
|
|
# target_path: join(conda_prefix, get_bin_directory_short_path(), 'activate') |
740
|
|
|
src_path = join(CONDA_PACKAGE_ROOT, 'shell', 'bin', 'activate') |
741
|
|
|
file_content = ( |
742
|
|
|
"#!/bin/sh\n" |
743
|
|
|
"_CONDA_ROOT=\"%s\"\n" |
744
|
|
|
) % conda_prefix |
745
|
|
|
with open(src_path) as fsrc: |
746
|
|
|
file_content += fsrc.read() |
747
|
|
|
return _install_file(target_path, file_content) |
748
|
|
|
|
749
|
|
|
|
750
|
|
|
def install_deactivate(target_path, conda_prefix): |
751
|
|
|
# target_path: join(conda_prefix, get_bin_directory_short_path(), 'deactivate') |
752
|
|
|
src_path = join(CONDA_PACKAGE_ROOT, 'shell', 'bin', 'deactivate') |
753
|
|
|
file_content = ( |
754
|
|
|
"#!/bin/sh\n" |
755
|
|
|
"_CONDA_ROOT=\"%s\"\n" |
756
|
|
|
) % conda_prefix |
757
|
|
|
with open(src_path) as fsrc: |
758
|
|
|
file_content += fsrc.read() |
759
|
|
|
return _install_file(target_path, file_content) |
760
|
|
|
|
761
|
|
|
|
762
|
|
|
def install_condacmd_conda_bat(target_path, conda_prefix): |
763
|
|
|
# target_path: join(conda_prefix, 'condacmd', 'conda.bat') |
764
|
|
|
conda_bat_src_path = join(CONDA_PACKAGE_ROOT, 'shell', 'condacmd', 'conda.bat') |
765
|
|
|
with open(conda_bat_src_path) as fsrc: |
766
|
|
|
file_content = fsrc.read() |
767
|
|
|
return _install_file(target_path, file_content) |
768
|
|
|
|
769
|
|
|
|
770
|
|
|
def install_condacmd_conda_activate_bat(target_path, conda_prefix): |
771
|
|
|
# target_path: join(conda_prefix, 'condacmd', '_conda_activate.bat') |
772
|
|
|
conda_bat_src_path = join(CONDA_PACKAGE_ROOT, 'shell', 'condacmd', '_conda_activate.bat') |
773
|
|
|
with open(conda_bat_src_path) as fsrc: |
774
|
|
|
file_content = fsrc.read() |
775
|
|
|
return _install_file(target_path, file_content) |
776
|
|
|
|
777
|
|
|
|
778
|
|
|
def install_condacmd_conda_auto_activate_bat(target_path, conda_prefix): |
779
|
|
|
# target_path: join(conda_prefix, 'condacmd', 'conda_auto_activate.bat') |
780
|
|
|
conda_bat_src_path = join(CONDA_PACKAGE_ROOT, 'shell', 'condacmd', 'conda_auto_activate.bat') |
781
|
|
|
with open(conda_bat_src_path) as fsrc: |
782
|
|
|
file_content = fsrc.read() |
783
|
|
|
return _install_file(target_path, file_content) |
784
|
|
|
|
785
|
|
|
|
786
|
|
|
def install_condacmd_hook_bat(target_path, conda_prefix): |
787
|
|
|
# target_path: join(conda_prefix, 'condacmd', 'conda_hook.bat') |
788
|
|
|
conda_bat_src_path = join(CONDA_PACKAGE_ROOT, 'shell', 'condacmd', 'conda_hook.bat') |
789
|
|
|
with open(conda_bat_src_path) as fsrc: |
790
|
|
|
file_content = fsrc.read() |
791
|
|
|
return _install_file(target_path, file_content) |
792
|
|
|
|
793
|
|
|
|
794
|
|
|
def install_conda_fish(target_path, conda_prefix): |
795
|
|
|
# target_path: join(conda_prefix, 'etc', 'fish', 'conf.d', 'conda.fish') |
796
|
|
|
file_content = FishActivator().hook(auto_activate_base=False) |
797
|
|
|
return _install_file(target_path, file_content) |
798
|
|
|
|
799
|
|
|
|
800
|
|
|
def install_conda_xsh(target_path, conda_prefix): |
801
|
|
|
# target_path: join(site_packages_dir, 'xonsh', 'conda.xsh') |
802
|
|
|
file_content = XonshActivator().hook(auto_activate_base=False) |
803
|
|
|
return _install_file(target_path, file_content) |
804
|
|
|
|
805
|
|
|
|
806
|
|
|
def install_conda_csh(target_path, conda_prefix): |
807
|
|
|
# target_path: join(conda_prefix, 'etc', 'profile.d', 'conda.csh') |
808
|
|
|
file_content = CshActivator().hook(auto_activate_base=False) |
809
|
|
|
return _install_file(target_path, file_content) |
810
|
|
|
|
811
|
|
|
|
812
|
|
|
def _config_fish_content(conda_prefix): |
813
|
|
|
if on_win: |
814
|
|
|
from ..activate import native_path_to_unix |
815
|
|
|
conda_exe = native_path_to_unix(join(conda_prefix, 'Scripts', 'conda.exe')) |
816
|
|
|
else: |
817
|
|
|
conda_exe = join(conda_prefix, 'bin', 'conda') |
818
|
|
|
conda_initialize_content = dals(""" |
819
|
|
|
# >>> conda initialize >>> |
820
|
|
|
# !! Contents within this block are managed by 'conda init' !! |
821
|
|
|
eval (eval %(conda_exe)s shell.fish hook $argv) |
822
|
|
|
# <<< conda initialize <<< |
823
|
|
|
""") % { |
824
|
|
|
'conda_exe': conda_exe, |
825
|
|
|
} |
826
|
|
|
return conda_initialize_content |
827
|
|
|
|
828
|
|
|
|
829
|
|
|
def init_fish_user(target_path, conda_prefix): |
830
|
|
|
# target_path: ~/.config/config.fish |
831
|
|
|
user_rc_path = target_path |
832
|
|
|
|
833
|
|
|
with open(user_rc_path) as fh: |
834
|
|
|
rc_content = fh.read() |
835
|
|
|
|
836
|
|
|
rc_original_content = rc_content |
837
|
|
|
|
838
|
|
|
conda_initialize_content = _config_fish_content(conda_prefix) |
839
|
|
|
|
840
|
|
|
if not on_win: |
841
|
|
|
rc_content = re.sub( |
842
|
|
|
r"^[ \t]*?(set -gx PATH ([\'\"]?).*?%s\/bin\2 [^\n]*?\$PATH)" |
843
|
|
|
r"" % basename(conda_prefix), |
844
|
|
|
r"# \1 # commented out by conda initialize", |
845
|
|
|
rc_content, |
846
|
|
|
flags=re.MULTILINE, |
847
|
|
|
) |
848
|
|
|
|
849
|
|
|
rc_content = re.sub( |
850
|
|
|
r"^[ \t]*[^#\n]?[ \t]*((?:source|\.) .*etc\/fish\/conf\.d\/conda\.fish.*?)\n" |
851
|
|
|
r"(conda activate.*?)$", |
852
|
|
|
r"# \1 # commented out by conda initialize\n# \2 # commented out by conda initialize", |
853
|
|
|
rc_content, |
854
|
|
|
flags=re.MULTILINE, |
855
|
|
|
) |
856
|
|
|
rc_content = re.sub( |
857
|
|
|
r"^[ \t]*[^#\n]?[ \t]*((?:source|\.) .*etc\/fish\/conda\.d\/conda\.fish.*?)$", |
858
|
|
|
r"# \1 # commented out by conda initialize", |
859
|
|
|
rc_content, |
860
|
|
|
flags=re.MULTILINE, |
861
|
|
|
) |
862
|
|
|
|
863
|
|
|
replace_str = "__CONDA_REPLACE_ME_123__" |
864
|
|
|
rc_content = re.sub( |
865
|
|
|
r"^# >>> conda initialize >>>$([\s\S]*?)# <<< conda initialize <<<\n$", |
866
|
|
|
replace_str, |
867
|
|
|
rc_content, |
868
|
|
|
flags=re.MULTILINE, |
869
|
|
|
) |
870
|
|
|
# TODO: maybe remove all but last of replace_str, if there's more than one occurrence |
871
|
|
|
rc_content = rc_content.replace(replace_str, conda_initialize_content) |
872
|
|
|
|
873
|
|
|
if "# >>> conda initialize >>>" not in rc_content: |
874
|
|
|
rc_content += '\n%s\n' % conda_initialize_content |
875
|
|
|
|
876
|
|
|
if rc_content != rc_original_content: |
877
|
|
|
if context.verbosity: |
878
|
|
|
print('\n') |
879
|
|
|
print(target_path) |
880
|
|
|
print(make_diff(rc_original_content, rc_content)) |
881
|
|
|
if not context.dry_run: |
882
|
|
|
with open(user_rc_path, 'w') as fh: |
883
|
|
|
fh.write(rc_content) |
884
|
|
|
return Result.MODIFIED |
885
|
|
|
else: |
886
|
|
|
return Result.NO_CHANGE |
887
|
|
|
|
888
|
|
|
|
889
|
|
|
def _bashrc_content(conda_prefix, shell): |
890
|
|
|
if on_win: |
891
|
|
|
from ..activate import native_path_to_unix |
892
|
|
|
conda_exe = native_path_to_unix(join(conda_prefix, 'Scripts', 'conda.exe')) |
893
|
|
|
conda_initialize_content = dals(""" |
894
|
|
|
# >>> conda initialize >>> |
895
|
|
|
# !! Contents within this block are managed by 'conda init' !! |
896
|
|
|
eval "$('%(conda_exe)s' shell.%(shell)s hook)" |
897
|
|
|
# <<< conda initialize <<< |
898
|
|
|
""") % { |
899
|
|
|
'conda_exe': conda_exe, |
900
|
|
|
'shell': shell, |
901
|
|
|
} |
902
|
|
|
else: |
903
|
|
|
conda_exe = join(conda_prefix, 'bin', 'conda') |
904
|
|
|
conda_initialize_content = dals(""" |
905
|
|
|
# >>> conda initialize >>> |
906
|
|
|
# !! Contents within this block are managed by 'conda init' !! |
907
|
|
|
__conda_setup="$('%(conda_exe)s' shell.%(shell)s hook 2> /dev/null)" |
908
|
|
|
if [ $? -eq 0 ]; then |
909
|
|
|
eval "$__conda_setup" |
910
|
|
|
else |
911
|
|
|
if [ -f "%(conda_prefix)s/etc/profile.d/conda.sh" ]; then |
912
|
|
|
. "%(conda_prefix)s/etc/profile.d/conda.sh" |
913
|
|
|
else |
914
|
|
|
export PATH="%(conda_bin)s:$PATH" |
915
|
|
|
fi |
916
|
|
|
fi |
917
|
|
|
unset __conda_setup |
918
|
|
|
# <<< conda initialize <<< |
919
|
|
|
""") % { |
920
|
|
|
'conda_exe': conda_exe, |
921
|
|
|
'shell': shell, |
922
|
|
|
'conda_bin': dirname(conda_exe), |
923
|
|
|
'conda_prefix': conda_prefix, |
924
|
|
|
} |
925
|
|
|
return conda_initialize_content |
926
|
|
|
|
927
|
|
|
|
928
|
|
|
def init_sh_user(target_path, conda_prefix, shell): |
929
|
|
|
# target_path: ~/.bash_profile |
930
|
|
|
user_rc_path = target_path |
931
|
|
|
|
932
|
|
|
with open(user_rc_path) as fh: |
933
|
|
|
rc_content = fh.read() |
934
|
|
|
|
935
|
|
|
rc_original_content = rc_content |
936
|
|
|
|
937
|
|
|
conda_initialize_content = _bashrc_content(conda_prefix, shell) |
938
|
|
|
|
939
|
|
|
if not on_win: |
940
|
|
|
rc_content = re.sub( |
941
|
|
|
r"^[ \t]*?(export PATH=[\'\"].*?%s\/bin:\$PATH[\'\"])" |
942
|
|
|
r"" % basename(conda_prefix), |
943
|
|
|
r"# \1 # commented out by conda initialize", |
944
|
|
|
rc_content, |
945
|
|
|
flags=re.MULTILINE, |
946
|
|
|
) |
947
|
|
|
|
948
|
|
|
rc_content = re.sub( |
949
|
|
|
r"^[ \t]*[^#\n]?[ \t]*((?:source|\.) .*etc\/profile\.d\/conda\.sh.*?)\n" |
950
|
|
|
r"(conda activate.*?)$", |
951
|
|
|
r"# \1 # commented out by conda initialize\n# \2 # commented out by conda initialize", |
952
|
|
|
rc_content, |
953
|
|
|
flags=re.MULTILINE, |
954
|
|
|
) |
955
|
|
|
rc_content = re.sub( |
956
|
|
|
r"^[ \t]*[^#\n]?[ \t]*((?:source|\.) .*etc\/profile\.d\/conda\.sh.*?)$", |
957
|
|
View Code Duplication |
r"# \1 # commented out by conda initialize", |
|
|
|
|
958
|
|
|
rc_content, |
959
|
|
|
flags=re.MULTILINE, |
960
|
|
|
) |
961
|
|
|
|
962
|
|
|
if on_win: |
963
|
|
|
rc_content = re.sub( |
964
|
|
|
r"^[ \t]*^[ \t]*[^#\n]?[ \t]*((?:source|\.) .*Scripts[/\\]activate.*?)$", |
965
|
|
|
r"# \1 # commented out by conda initialize", |
966
|
|
|
rc_content, |
967
|
|
|
flags=re.MULTILINE, |
968
|
|
|
) |
969
|
|
|
else: |
970
|
|
|
rc_content = re.sub( |
971
|
|
|
r"^[ \t]*^[ \t]*[^#\n]?[ \t]*((?:source|\.) .*bin/activate.*?)$", |
972
|
|
|
r"# \1 # commented out by conda initialize", |
973
|
|
|
rc_content, |
974
|
|
|
flags=re.MULTILINE, |
975
|
|
|
) |
976
|
|
|
|
977
|
|
|
replace_str = "__CONDA_REPLACE_ME_123__" |
978
|
|
|
rc_content = re.sub( |
979
|
|
|
r"^# >>> conda initialize >>>$([\s\S]*?)# <<< conda initialize <<<\n$", |
980
|
|
|
replace_str, |
981
|
|
|
rc_content, |
982
|
|
|
flags=re.MULTILINE, |
983
|
|
|
) |
984
|
|
|
# TODO: maybe remove all but last of replace_str, if there's more than one occurrence |
985
|
|
|
rc_content = rc_content.replace(replace_str, conda_initialize_content) |
986
|
|
|
|
987
|
|
|
if "# >>> conda initialize >>>" not in rc_content: |
988
|
|
|
rc_content += '\n%s\n' % conda_initialize_content |
989
|
|
|
|
990
|
|
|
if rc_content != rc_original_content: |
991
|
|
|
if context.verbosity: |
992
|
|
|
print('\n') |
993
|
|
|
print(target_path) |
994
|
|
|
print(make_diff(rc_original_content, rc_content)) |
995
|
|
|
if not context.dry_run: |
996
|
|
|
with open(user_rc_path, 'w') as fh: |
997
|
|
|
fh.write(rc_content) |
998
|
|
|
return Result.MODIFIED |
999
|
|
|
else: |
1000
|
|
|
return Result.NO_CHANGE |
1001
|
|
|
|
1002
|
|
|
|
1003
|
|
|
def init_sh_system(target_path, conda_prefix): |
1004
|
|
|
# target_path: '/etc/profile.d/conda.sh' |
1005
|
|
|
conda_sh_system_path = target_path |
1006
|
|
|
|
1007
|
|
|
if exists(conda_sh_system_path): |
1008
|
|
|
with open(conda_sh_system_path) as fh: |
1009
|
|
|
conda_sh_system_contents = fh.read() |
1010
|
|
|
else: |
1011
|
|
|
conda_sh_system_contents = "" |
1012
|
|
|
conda_sh_contents = _bashrc_content(conda_prefix, 'posix') |
1013
|
|
View Code Duplication |
if conda_sh_system_contents != conda_sh_contents: |
|
|
|
|
1014
|
|
|
if context.verbosity: |
1015
|
|
|
print('\n') |
1016
|
|
|
print(target_path) |
1017
|
|
|
print(make_diff(conda_sh_contents, conda_sh_system_contents)) |
1018
|
|
|
if not context.dry_run: |
1019
|
|
|
if lexists(conda_sh_system_path): |
1020
|
|
|
rm_rf(conda_sh_system_path) |
1021
|
|
|
mkdir_p(dirname(conda_sh_system_path)) |
1022
|
|
|
with open(conda_sh_system_path, 'w') as fh: |
1023
|
|
|
fh.write(conda_sh_contents) |
1024
|
|
|
return Result.MODIFIED |
1025
|
|
|
else: |
1026
|
|
|
return Result.NO_CHANGE |
1027
|
|
|
|
1028
|
|
|
|
1029
|
|
|
def _read_windows_registry(target_path): # pragma: no cover |
1030
|
|
|
# HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\AutoRun |
1031
|
|
|
# HKEY_CURRENT_USER\Software\Microsoft\Command Processor\AutoRun |
1032
|
|
|
# returns value_value, value_type -or- None, None if target does not exist |
1033
|
|
|
main_key, the_rest = target_path.split('\\', 1) |
1034
|
|
|
subkey_str, value_name = the_rest.rsplit('\\', 1) |
1035
|
|
|
main_key = getattr(winreg, main_key) |
1036
|
|
|
try: |
1037
|
|
|
key = winreg.OpenKey(main_key, subkey_str, 0, winreg.KEY_READ) |
1038
|
|
|
except EnvironmentError as e: |
1039
|
|
|
if e.errno != ENOENT: |
1040
|
|
|
raise |
1041
|
|
|
return None, None |
1042
|
|
|
try: |
1043
|
|
|
value_tuple = winreg.QueryValueEx(key, value_name) |
1044
|
|
|
value_value = value_tuple[0].strip() |
1045
|
|
|
value_type = value_tuple[1] |
1046
|
|
|
return value_value, value_type |
1047
|
|
|
finally: |
1048
|
|
|
winreg.CloseKey(key) |
1049
|
|
|
|
1050
|
|
|
|
1051
|
|
|
def _write_windows_registry(target_path, value_value, value_type): # pragma: no cover |
1052
|
|
|
main_key, the_rest = target_path.split('\\', 1) |
1053
|
|
|
subkey_str, value_name = the_rest.rsplit('\\', 1) |
1054
|
|
|
main_key = getattr(winreg, main_key) |
1055
|
|
|
try: |
1056
|
|
|
key = winreg.OpenKey(main_key, subkey_str, 0, winreg.KEY_WRITE) |
1057
|
|
|
except EnvironmentError as e: |
1058
|
|
|
if e.errno != ENOENT: |
1059
|
|
|
raise |
1060
|
|
|
key = winreg.CreateKey(main_key, subkey_str) |
1061
|
|
|
try: |
1062
|
|
|
winreg.SetValueEx(key, value_name, 0, value_type, value_value) |
1063
|
|
|
finally: |
1064
|
|
|
winreg.CloseKey(key) |
1065
|
|
|
|
1066
|
|
|
|
1067
|
|
|
def init_cmd_exe_registry(target_path, conda_prefix): |
1068
|
|
|
# HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\AutoRun |
1069
|
|
|
# HKEY_CURRENT_USER\Software\Microsoft\Command Processor\AutoRun |
1070
|
|
|
|
1071
|
|
|
prev_value, value_type = _read_windows_registry(target_path) |
1072
|
|
|
if prev_value is None: |
1073
|
|
|
prev_value = "" |
1074
|
|
|
value_type = winreg.REG_EXPAND_SZ |
1075
|
|
|
|
1076
|
|
|
hook_path = '"%s"' % join(conda_prefix, 'condacmd', 'conda_hook.bat') |
1077
|
|
|
replace_str = "__CONDA_REPLACE_ME_123__" |
1078
|
|
|
new_value = re.sub( |
1079
|
|
|
r'(\".*?conda[-_]hook\.bat\")', |
1080
|
|
|
replace_str, |
1081
|
|
|
prev_value, |
1082
|
|
|
count=1, |
1083
|
|
|
flags=re.IGNORECASE | re.UNICODE, |
1084
|
|
|
) |
1085
|
|
|
new_value = new_value.replace(replace_str, hook_path) |
1086
|
|
|
if hook_path not in new_value: |
1087
|
|
|
if new_value: |
1088
|
|
|
new_value += ' & ' + hook_path |
1089
|
|
|
else: |
1090
|
|
View Code Duplication |
new_value = hook_path |
|
|
|
|
1091
|
|
|
|
1092
|
|
|
if prev_value != new_value: |
1093
|
|
|
if context.verbosity: |
1094
|
|
|
print('\n') |
1095
|
|
|
print(target_path) |
1096
|
|
|
print(make_diff(prev_value, new_value)) |
1097
|
|
|
if not context.dry_run: |
1098
|
|
|
_write_windows_registry(target_path, new_value, value_type) |
1099
|
|
|
return Result.MODIFIED |
1100
|
|
|
else: |
1101
|
|
|
return Result.NO_CHANGE |
1102
|
|
|
|
1103
|
|
|
|
1104
|
|
|
def remove_conda_in_sp_dir(target_path): |
1105
|
|
|
# target_path: site_packages_dir |
1106
|
|
|
modified = False |
1107
|
|
|
site_packages_dir = target_path |
1108
|
|
|
rm_rf_these = chain.from_iterable(( |
1109
|
|
|
glob(join(site_packages_dir, "conda-*info")), |
1110
|
|
|
glob(join(site_packages_dir, "conda.*")), |
1111
|
|
|
glob(join(site_packages_dir, "conda-*.egg")), |
1112
|
|
|
)) |
1113
|
|
|
rm_rf_these = (p for p in rm_rf_these if not p.endswith('conda.egg-link')) |
1114
|
|
|
for fn in rm_rf_these: |
1115
|
|
|
print("rm -rf %s" % join(site_packages_dir, fn), file=sys.stderr) |
1116
|
|
|
if not context.dry_run: |
1117
|
|
|
rm_rf(join(site_packages_dir, fn)) |
1118
|
|
|
modified = True |
1119
|
|
|
others = ( |
1120
|
|
|
"conda", |
1121
|
|
|
"conda_env", |
1122
|
|
|
) |
1123
|
|
|
for other in others: |
1124
|
|
|
path = join(site_packages_dir, other) |
1125
|
|
|
if lexists(path): |
1126
|
|
|
print("rm -rf %s" % path, file=sys.stderr) |
1127
|
|
|
if not context.dry_run: |
1128
|
|
|
rm_rf(path) |
1129
|
|
|
modified = True |
1130
|
|
|
if modified: |
1131
|
|
|
return Result.MODIFIED |
1132
|
|
|
else: |
1133
|
|
|
return Result.NO_CHANGE |
1134
|
|
|
|
1135
|
|
|
|
1136
|
|
|
def make_conda_egg_link(target_path, conda_source_root): |
1137
|
|
|
# target_path: join(site_packages_dir, 'conda.egg-link') |
1138
|
|
|
conda_egg_link_contents = conda_source_root + os.linesep |
1139
|
|
|
|
1140
|
|
|
if isfile(target_path): |
1141
|
|
View Code Duplication |
with open(target_path) as fh: |
|
|
|
|
1142
|
|
|
conda_egg_link_contents_old = fh.read() |
1143
|
|
|
else: |
1144
|
|
|
conda_egg_link_contents_old = "" |
1145
|
|
|
|
1146
|
|
|
if conda_egg_link_contents_old != conda_egg_link_contents: |
1147
|
|
|
if context.verbosity: |
1148
|
|
|
print('\n', file=sys.stderr) |
1149
|
|
|
print(target_path, file=sys.stderr) |
1150
|
|
|
print(make_diff(conda_egg_link_contents_old, conda_egg_link_contents), file=sys.stderr) |
1151
|
|
|
if not context.dry_run: |
1152
|
|
|
with open(target_path, 'w') as fh: |
1153
|
|
|
fh.write(ensure_fs_path_encoding(conda_egg_link_contents)) |
1154
|
|
|
return Result.MODIFIED |
1155
|
|
|
else: |
1156
|
|
|
return Result.NO_CHANGE |
1157
|
|
|
|
1158
|
|
|
|
1159
|
|
|
def modify_easy_install_pth(target_path, conda_source_root): |
1160
|
|
|
# target_path: join(site_packages_dir, 'easy-install.pth') |
1161
|
|
|
easy_install_new_line = conda_source_root |
1162
|
|
|
|
1163
|
|
|
if isfile(target_path): |
1164
|
|
|
with open(target_path) as fh: |
1165
|
|
|
old_contents = fh.read() |
1166
|
|
|
else: |
1167
|
|
|
old_contents = "" |
1168
|
|
|
|
1169
|
|
|
old_contents_lines = old_contents.splitlines() |
1170
|
|
|
if easy_install_new_line in old_contents_lines: |
1171
|
|
|
return Result.NO_CHANGE |
1172
|
|
|
|
1173
|
|
|
ln_end = os.sep + "conda" |
1174
|
|
|
old_contents_lines = tuple(ln for ln in old_contents_lines if not ln.endswith(ln_end)) |
1175
|
|
|
new_contents = easy_install_new_line + '\n' + '\n'.join(old_contents_lines) + '\n' |
1176
|
|
|
|
1177
|
|
|
if context.verbosity: |
1178
|
|
|
print('\n', file=sys.stderr) |
1179
|
|
|
print(target_path, file=sys.stderr) |
1180
|
|
|
print(make_diff(old_contents, new_contents), file=sys.stderr) |
1181
|
|
|
if not context.dry_run: |
1182
|
|
|
with open(target_path, 'w') as fh: |
1183
|
|
|
fh.write(ensure_fs_path_encoding(new_contents)) |
1184
|
|
|
return Result.MODIFIED |
1185
|
|
|
|
1186
|
|
|
|
1187
|
|
|
def make_dev_egg_info_file(target_path): |
1188
|
|
|
# target_path: join(conda_source_root, 'conda.egg-info') |
1189
|
|
|
|
1190
|
|
|
if isfile(target_path): |
1191
|
|
|
with open(target_path) as fh: |
1192
|
|
|
old_contents = fh.read() |
1193
|
|
|
else: |
1194
|
|
|
old_contents = "" |
1195
|
|
|
|
1196
|
|
|
new_contents = dals(""" |
1197
|
|
|
Metadata-Version: 1.1 |
1198
|
|
|
Name: conda |
1199
|
|
|
Version: %s |
1200
|
|
|
Platform: UNKNOWN |
1201
|
|
|
Summary: OS-agnostic, system-level binary package manager. |
1202
|
|
|
""") % CONDA_VERSION |
1203
|
|
|
|
1204
|
|
|
if old_contents == new_contents: |
1205
|
|
|
return Result.NO_CHANGE |
1206
|
|
|
|
1207
|
|
|
if context.verbosity: |
1208
|
|
|
print('\n', file=sys.stderr) |
1209
|
|
|
print(target_path, file=sys.stderr) |
1210
|
|
|
print(make_diff(old_contents, new_contents), file=sys.stderr) |
1211
|
|
|
if not context.dry_run: |
1212
|
|
|
if lexists(target_path): |
1213
|
|
|
rm_rf(target_path) |
1214
|
|
|
with open(target_path, 'w') as fh: |
1215
|
|
|
fh.write(new_contents) |
1216
|
|
|
return Result.MODIFIED |
1217
|
|
|
|
1218
|
|
|
|
1219
|
|
|
# ##################################################### |
1220
|
|
|
# helper functions |
1221
|
|
|
# ##################################################### |
1222
|
|
|
|
1223
|
|
|
def make_diff(old, new): |
1224
|
|
|
return '\n'.join(unified_diff(old.splitlines(), new.splitlines())) |
1225
|
|
|
|
1226
|
|
|
|
1227
|
|
|
def _get_python_info(prefix): |
1228
|
|
|
python_exe = join(prefix, get_python_short_path()) |
1229
|
|
|
result = subprocess_call("%s --version" % python_exe) |
1230
|
|
|
stdout, stderr = result.stdout.strip(), result.stderr.strip() |
1231
|
|
|
if stderr: |
1232
|
|
|
python_version = stderr.split()[1] |
1233
|
|
|
elif stdout: # pragma: no cover |
1234
|
|
|
python_version = stdout.split()[1] |
1235
|
|
|
else: # pragma: no cover |
1236
|
|
|
raise ValueError("No python version information available.") |
1237
|
|
|
|
1238
|
|
|
site_packages_dir = join(prefix, |
1239
|
|
|
win_path_ok(get_python_site_packages_short_path(python_version))) |
1240
|
|
|
return python_exe, python_version, site_packages_dir |
1241
|
|
|
|
1242
|
|
|
|
1243
|
|
|
if __name__ == "__main__": |
1244
|
|
|
if on_win: |
1245
|
|
|
temp_path = sys.argv[1] |
1246
|
|
|
run_plan_from_temp_file(temp_path) |
1247
|
|
|
else: |
1248
|
|
|
run_plan_from_stdin() |
1249
|
|
|
|