Completed
Push — master ( 5dabaa...1f3ca6 )
by Kale
62:52
created

run_plan_elevated()   D

Complexity

Conditions 10

Size

Total Lines 60

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 10
c 1
b 0
f 1
dl 0
loc 60
rs 4.8

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like run_plan_elevated() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
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
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
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",
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
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:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
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
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
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:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
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