Completed
Push — master ( 082fe8...6cfd3b )
by Kale
62:26
created

ArgumentParser.error()   D

Complexity

Conditions 8

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 40.768

Importance

Changes 0
Metric Value
cc 8
c 0
b 0
f 0
dl 0
loc 28
rs 4
ccs 4
cts 20
cp 0.2
crap 40.768
1
# -*- coding: utf-8 -*-
2
# Copyright (C) 2012 Anaconda, Inc
3
# SPDX-License-Identifier: BSD-3-Clause
4
from __future__ import absolute_import, division, print_function, unicode_literals
5
6
from argparse import (ArgumentParser as ArgumentParserBase, REMAINDER, RawDescriptionHelpFormatter,
7 7
                      SUPPRESS, _CountAction, _HelpAction)
8
from logging import getLogger
9 7
import os
10 7
from os.path import abspath, expanduser, join
11 7
from subprocess import Popen
12 7
import sys
13 7
from textwrap import dedent
14
15 7
from .. import __version__
16 7
from ..base.constants import COMPATIBLE_SHELLS, CONDA_HOMEPAGE_URL, DepsModifier, UpdateModifier
17 7
from ..common.constants import NULL
18
19 7
log = getLogger(__name__)
20
21
# duplicated code in the interest of import efficiency
22 7
on_win = bool(sys.platform == "win32")
23 7
user_rc_path = abspath(expanduser('~/.condarc'))
24
escaped_user_rc_path = user_rc_path.replace("%", "%%")
25
escaped_sys_rc_path = abspath(join(sys.prefix, '.condarc')).replace("%", "%%")
26
27
28
def generate_parser():
29
    p = ArgumentParser(
30
        description='conda is a tool for managing and deploying applications,'
31
                    ' environments and packages.',
32
    )
33 7
    p.add_argument(
34 7
        '-V', '--version',
35
        action='version',
36 7
        version='conda %s' % __version__,
37
        help="Show the conda version number and exit."
38
    )
39 7
    p.add_argument(
40
        "--debug",
41 7
        action="store_true",
42
        help=SUPPRESS,
43
    )
44
    p.add_argument(
45
        "--json",
46
        action="store_true",
47
        help=SUPPRESS,
48
    )
49
    sub_parsers = p.add_subparsers(
50
        metavar='command',
51
        dest='cmd',
52
    )
53
    # http://bugs.python.org/issue9253
54
    # http://stackoverflow.com/a/18283730/1599393
55
    sub_parsers.required = True
56
57
    configure_parser_clean(sub_parsers)
58
    configure_parser_config(sub_parsers)
59
    configure_parser_create(sub_parsers)
60
    configure_parser_help(sub_parsers)
61
    configure_parser_info(sub_parsers)
62
    configure_parser_init(sub_parsers)
63
    configure_parser_install(sub_parsers)
64
    configure_parser_list(sub_parsers)
65
    configure_parser_package(sub_parsers)
66
    configure_parser_remove(sub_parsers)
67
    configure_parser_remove(sub_parsers, name='uninstall')
68
    configure_parser_run(sub_parsers)
69
    configure_parser_search(sub_parsers)
70
    configure_parser_update(sub_parsers)
71
    configure_parser_update(sub_parsers, name='upgrade')
72
73
    return p
74
75
76
def do_call(args, parser):
77
    relative_mod, func_name = args.func.rsplit('.', 1)
78
    # func_name should always be 'execute'
79 7
    from importlib import import_module
80 7
    module = import_module(relative_mod, __name__.rsplit('.', 1)[0])
81 7
    exit_code = getattr(module, func_name)(args, parser)
82 7
    return exit_code
83 7
84 7
85 7
class ArgumentParser(ArgumentParserBase):
86
    def __init__(self, *args, **kwargs):
87 7
        if not kwargs.get('formatter_class'):
88 7
            kwargs['formatter_class'] = RawDescriptionHelpFormatter
89
        if 'add_help' not in kwargs:
90 7
            add_custom_help = True
91 7
            kwargs['add_help'] = False
92
        else:
93 7
            add_custom_help = False
94 7
        super(ArgumentParser, self).__init__(*args, **kwargs)
95
96 7
        if add_custom_help:
97
            add_parser_help(self)
98
99
        if self.description:
100
            self.description += "\n\nOptions:\n"
101 7
102 7
    def _get_action_from_name(self, name):
103
        """Given a name, get the Action instance registered with this parser.
104 7
        If only it were made available in the ArgumentError object. It is
105 7
        passed as it's first arg...
106 7
        """
107 7
        container = self._actions
108
        if name is None:
109 7
            return None
110
        for action in container:
111
            if '/'.join(action.option_strings) == name:
112 7
                return action
113 7
            elif action.metavar == name:
114 7
                return action
115 7
            elif action.dest == name:
116
                return action
117 7
118 7
    def error(self, message):
119
        import re
120
        from .find_commands import find_executable
121 7
        exc = sys.exc_info()[1]
122 7
        if exc:
123
            # this is incredibly lame, but argparse stupidly does not expose
124
            # reasonable hooks for customizing error handling
125 7
            if hasattr(exc, 'argument_name'):
126
                argument = self._get_action_from_name(exc.argument_name)
127
            else:
128
                argument = None
129
            if argument and argument.dest == "cmd":
130
                m = re.match(r"invalid choice: u?'([\w\-]*?)'", exc.message)
131
                if m:
132
                    cmd = m.group(1)
133
                    if not cmd:
134
                        self.print_help()
135
                        sys.exit(0)
136
                    else:
137
                        executable = find_executable('conda-' + cmd)
138
                        if not executable:
139
                            from ..exceptions import CommandNotFoundError
140
                            raise CommandNotFoundError(cmd)
141
                        args = [find_executable('conda-' + cmd)]
142
                        args.extend(sys.argv[2:])
143
                        _exec(args, os.environ)
144
145
        super(ArgumentParser, self).error(message)
146
147
    def print_help(self):
148
        super(ArgumentParser, self).print_help()
149
150
        if sys.argv[1:] in ([], [''], ['help'], ['-h'], ['--help']):
151
            from .find_commands import find_commands
152
            other_commands = find_commands()
153
            if other_commands:
154
                builder = ['']
155
                builder.append("conda commands available from other packages:")
156
                builder.extend('  %s' % cmd for cmd in sorted(other_commands))
157 7
                print('\n'.join(builder))
158
159 7
160
def _exec(executable_args, env_vars):
161
    return (_exec_win if on_win else _exec_unix)(executable_args, env_vars)
162
163
164
def _exec_win(executable_args, env_vars):
165
    p = Popen(executable_args, env=env_vars)
166
    try:
167
        p.communicate()
168 7
    except KeyboardInterrupt:
169 7
        p.wait()
170
    finally:
171
        sys.exit(p.returncode)
172 7
173
174
def _exec_unix(executable_args, env_vars):
175
    os.execvpe(executable_args[0], executable_args, env_vars)
176
177
178
class NullCountAction(_CountAction):
179
180
    @staticmethod
181
    def _ensure_value(namespace, name, value):
182
        if getattr(namespace, name, NULL) in (NULL, None):
183
            setattr(namespace, name, value)
184
        return getattr(namespace, name)
185
186
    def __call__(self, parser, namespace, values, option_string=None):
187
        new_count = self._ensure_value(namespace, self.dest, 0) + 1
188
        setattr(namespace, self.dest, new_count)
189
190
191
# #############################################################################################
192
#
193
# sub-parsers
194
#
195
# #############################################################################################
196
197
def configure_parser_clean(sub_parsers):
198
    descr = dedent("""
199
    Remove unused packages and caches.
200
    """)
201
    example = dedent("""
202
    Examples:
203 View Code Duplication
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
204
        conda clean --tarballs
205
    """)
206
    p = sub_parsers.add_parser(
207
        'clean',
208
        description=descr,
209
        help=descr,
210
        epilog=example,
211
    )
212
213
    removal_target_options = p.add_argument_group("Removal Targets")
214
    removal_target_options.add_argument(
215
        "-a", "--all",
216
        action="store_true",
217
        help="Remove index cache, lock files, unused cache packages, and tarballs.",
218
    )
219
    removal_target_options.add_argument(
220
        "-i", "--index-cache",
221
        action="store_true",
222
        help="Remove index cache.",
223
    )
224
    removal_target_options.add_argument(
225
        "-l", "--lock",
226
        action="store_true",
227
        help="Remove all conda lock files.",
228
    )
229
    removal_target_options.add_argument(
230
        '-p', '--packages',
231
        action='store_true',
232
        help="Remove unused cached packages. Warning: This does not check for symlinked packages.",
233
    )
234
    removal_target_options.add_argument(
235
        '-s', '--source-cache',
236
        action='store_true',
237
        # help="Remove files from the source cache of conda build.",
238
        help=SUPPRESS,
239
    )
240
    removal_target_options.add_argument(
241
        "-t", "--tarballs",
242
        action="store_true",
243
        help="Remove cached package tarballs.",
244
    )
245
246
    add_output_and_prompt_options(p)
247
248
    p.set_defaults(func='.main_clean.execute')
249
250
251
def configure_parser_info(sub_parsers):
252
    help = "Display information about current conda install."
253
254
    p = sub_parsers.add_parser(
255
        'info',
256
        description=help,
257
        help=help,
258
    )
259
    add_parser_json(p)
260
    p.add_argument(
261
        "--offline",
262
        action='store_true',
263
        default=NULL,
264
        help=SUPPRESS,
265
    )
266
    p.add_argument(
267
        '-a', "--all",
268
        action="store_true",
269
        help="Show all information.",
270
    )
271
    p.add_argument(
272
        '--base',
273
        action='store_true',
274
        help='Display base environment path.',
275
    )
276
    # TODO: deprecate 'conda info --envs' and create 'conda list --envs'
277
    p.add_argument(
278
        '-e', "--envs",
279
        action="store_true",
280
        help="List all known conda environments.",
281
    )
282
    p.add_argument(
283
        '-l', "--license",
284
        action="store_true",
285
        help=SUPPRESS,
286
    )
287
    p.add_argument(
288
        '-s', "--system",
289
        action="store_true",
290
        help="List environment variables.",
291
    )
292
    p.add_argument(
293
        '--root',
294
        action='store_true',
295
        help=SUPPRESS,
296
        dest='base',
297
    )
298
    p.add_argument(
299
        '--unsafe-channels',
300
        action='store_true',
301
        help='Display list of channels with tokens exposed.',
302
    )
303
304
    # TODO: deprecate 'conda info <PACKAGE>'
305
    p.add_argument(
306
        'packages',
307
        action="store",
308
        nargs='*',
309
        help="Display information about packages.",
310
    )
311
312
    p.set_defaults(func='.main_info.execute')
313
314
315
def configure_parser_config(sub_parsers):
316
    descr = dedent("""
317
    Modify configuration values in .condarc.  This is modeled after the git
318
    config command.  Writes to the user .condarc file (%s) by default.
319
320
    """) % escaped_user_rc_path
321
322
    # Note, the extra whitespace in the list keys is on purpose. It's so the
323
    # formatting from help2man is still valid YAML (otherwise it line wraps the
324
    # keys like "- conda - defaults"). Technically the parser here still won't
325
    # recognize it because it removes the indentation, but at least it will be
326
    # valid.
327
    additional_descr = dedent("""
328
    See `conda config --describe` or %s/docs/config.html
329
    for details on all the options that can go in .condarc.
330
331
    Examples:
332
333
    Display all configuration values as calculated and compiled:
334
335
        conda config --show
336
337
    Display all identified configuration sources:
338
339
        conda config --show-sources
340
341
    Describe all available configuration options:
342
343
        conda config --describe
344
345
    Add the conda-canary channel:
346
347
        conda config --add channels conda-canary
348
349
    Set the output verbosity to level 3 (highest) for the current activate environment:
350
351
        conda config --set verbosity 3 --env
352
353
    Add the 'conda-forge' channel as a backup to 'defaults':
354
355
        conda config --append channels conda-forge
356
357
    """) % CONDA_HOMEPAGE_URL
358
359
    p = sub_parsers.add_parser(
360
        'config',
361
        description=descr,
362
        help=descr,
363
        epilog=additional_descr,
364
    )
365
    add_parser_json(p)
366
367
    # TODO: use argparse.FileType
368
    config_file_location_group = p.add_argument_group(
369
        'Config File Location Selection',
370
        "Without one of these flags, the user config file at '%s' is used." % escaped_user_rc_path
371
    )
372
    location = config_file_location_group.add_mutually_exclusive_group()
373
    location.add_argument(
374
        "--system",
375
        action="store_true",
376
        help="Write to the system .condarc file at '%s'." % escaped_sys_rc_path,
377
    )
378
    location.add_argument(
379
        "--env",
380
        action="store_true",
381
        help="Write to the active conda environment .condarc file (%s). "
382
             "If no environment is active, write to the user config file (%s)."
383
             "" % (
384
                 os.getenv('CONDA_PREFIX', "<no active environment>").replace("%", "%%"),
385
                 escaped_user_rc_path,
386
             ),
387
    )
388
    location.add_argument(
389
        "--file",
390
        action="store",
391
        help="Write to the given file."
392
    )
393
394
    # XXX: Does this really have to be mutually exclusive. I think the below
395
    # code will work even if it is a regular group (although combination of
396
    # --add and --remove with the same keys will not be well-defined).
397
    _config_subcommands = p.add_argument_group("Config Subcommands")
398
    config_subcommands = _config_subcommands.add_mutually_exclusive_group()
399
    config_subcommands.add_argument(
400
        "--show",
401
        nargs='*',
402
        default=None,
403
        help="Display configuration values as calculated and compiled. "
404
             "If no arguments given, show information for all configuration values.",
405
    )
406
    config_subcommands.add_argument(
407
        "--show-sources",
408
        action="store_true",
409
        help="Display all identified configuration sources.",
410
    )
411
    config_subcommands.add_argument(
412
        "--validate",
413
        action="store_true",
414
        help="Validate all configuration sources.",
415
    )
416
    config_subcommands.add_argument(
417
        "--describe",
418
        nargs='*',
419
        default=None,
420
        help="Describe given configuration parameters. If no arguments given, show "
421
             "information for all configuration parameters.",
422
    )
423
    config_subcommands.add_argument(
424
        "--write-default",
425
        action="store_true",
426
        help="Write the default configuration to a file. "
427
             "Equivalent to `conda config --describe > ~/.condarc`.",
428
    )
429
430
    _config_modifiers = p.add_argument_group("Config Modifiers")
431
    config_modifiers = _config_modifiers.add_mutually_exclusive_group()
432
    config_modifiers.add_argument(
433
        "--get",
434
        nargs='*',
435
        action="store",
436
        help="Get a configuration value.",
437
        default=None,
438
        metavar='KEY',
439
    )
440
    config_modifiers.add_argument(
441
        "--append",
442
        nargs=2,
443
        action="append",
444
        help="""Add one configuration value to the end of a list key.""",
445
        default=[],
446
        metavar=('KEY', 'VALUE'),
447
    )
448
    config_modifiers.add_argument(
449
        "--prepend", "--add",
450
        nargs=2,
451
        action="append",
452
        help="""Add one configuration value to the beginning of a list key.""",
453
        default=[],
454
        metavar=('KEY', 'VALUE'),
455
    )
456
    config_modifiers.add_argument(
457
        "--set",
458
        nargs=2,
459
        action="append",
460
        help="""Set a boolean or string key""",
461
        default=[],
462
        metavar=('KEY', 'VALUE'),
463
    )
464
    config_modifiers.add_argument(
465
        "--remove",
466
        nargs=2,
467
        action="append",
468
        help="""Remove a configuration value from a list key. This removes
469
    all instances of the value.""",
470
        default=[],
471
        metavar=('KEY', 'VALUE'),
472
    )
473
    config_modifiers.add_argument(
474
        "--remove-key",
475
        nargs=1,
476
        action="append",
477
        help="""Remove a configuration key (and all its values).""",
478
        default=[],
479
        metavar="KEY",
480
    )
481
    config_modifiers.add_argument(
482
        "--stdin",
483
        action="store_true",
484
        help="Apply configuration information given in yaml format piped through stdin.",
485
    )
486
487
    p.add_argument(
488
        "-f", "--force",
489
        action="store_true",
490
        default=NULL,
491
        help=SUPPRESS,  # TODO: No longer used.  Remove in a future release.
492
    )
493
494
    p.set_defaults(func='.main_config.execute')
495
496
497 View Code Duplication
def configure_parser_create(sub_parsers):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
498
    help = "Create a new conda environment from a list of specified packages. "
499
    descr = (help +
500
             "To use the created environment, use 'source activate "
501
             "envname' look in that directory first.  This command requires either "
502
             "the -n NAME or -p PREFIX option.")
503
504
    example = dedent("""
505
    Examples:
506
507
        conda create -n myenv sqlite
508
509
    """)
510
    p = sub_parsers.add_parser(
511
        'create',
512
        description=descr,
513
        help=help,
514
        epilog=example,
515
    )
516
    p.add_argument(
517
        "--clone",
518
        action="store",
519
        help='Path to (or name of) existing local environment.',
520
        metavar='ENV',
521
    )
522
    solver_mode_options, package_install_options = add_parser_create_install_update(p)
523
    solver_mode_options.add_argument(
524
        "--no-default-packages",
525
        action="store_true",
526
        help='Ignore create_default_packages in the .condarc file.',
527
    )
528
    p.add_argument(
529
        '-m', "--mkdir",
530
        action="store_true",
531
        help=SUPPRESS,
532
    )
533
    p.set_defaults(func='.main_create.execute')
534
535
536
def configure_parser_init(sub_parsers):
537
    help = "Initialize conda for shell interaction. [Experimental]"
538
    descr = help
539
540
    epilog = dedent("""
541
    Key parts of conda's functionality require that it interact directly with the shell
542
    within which conda is being invoked. The `conda activate` and `conda deactivate` commands
543
    specifically are shell-level commands. That is, they affect the state (e.g. environment
544
    variables) of the shell context being interacted with. Other core commands, like
545
    `conda create` and `conda install`, also necessarily interact with the shell environment.
546
    They're therefore implemented in ways specific to each shell. Each shell must be configured
547
    to make use of them.
548
549
    This command makes changes to your system that are specific and customized for each shell.
550
    To see the specific files and locations on your system that will be affected before, use the
551
    '--dry-run' flag.  To see the exact changes that are being or will be made to each location,
552
    use the '--verbose' flag.
553
554
    IMPORTANT: After running `conda init`, most shells will need to be closed and restarted
555
               for changes to take effect.
556
557
    """)
558
559
    # dev_example = dedent("""
560
    #     # An example for creating an environment to develop on conda's own code. Clone the
561
    #     # conda repo and install a dedicated miniconda within it. Remove all remnants of
562
    #     # conda source files in the `site-packages` directory associated with
563
    #     # `~/conda/devenv/bin/python`. Write a `conda.pth` file in that `site-packages`
564
    #     # directory pointing to source code in `~/conda`, the current working directory.
565
    #     # Write commands to stdout, suitable for bash `eval`, that sets up the current
566
    #     # shell as a dev environment.
567
    #
568
    #         $ CONDA_PROJECT_ROOT="~/conda"
569
    #         $ git clone [email protected]:conda/conda "$CONDA_PROJECT_ROOT"
570
    #         $ cd "$CONDA_PROJECT_ROOT"
571
    #         $ wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh
572
    #         $ bash Miniconda3-latest-Linux-x86_64.sh -bfp ./devenv
573
    #         $ eval "$(./devenv/bin/python -m conda init --dev bash)"
574
    #
575
    #
576
    # """)
577
578
    p = sub_parsers.add_parser(
579
        'init',
580
        description=descr,
581
        help=help,
582
        epilog=epilog,
583
    )
584
585
    p.add_argument(
586
        "--dev",
587
        action="store_true",
588
        help=SUPPRESS,
589
        default=NULL,
590
    )
591
592
    p.add_argument(
593
        "--all",
594
        action="store_true",
595
        help="Initialize all currently available shells.",
596
        default=NULL,
597
    )
598
599
    setup_type_group = p.add_argument_group('setup type')
600
    setup_type_group.add_argument(
601
        "--install",
602
        action="store_true",
603
        help=SUPPRESS,
604
        default=NULL,
605
    )
606
    setup_type_group.add_argument(
607
        "--user",
608
        action="store_true",
609
        # help="Initialize conda for the current user (default).",
610
        help=SUPPRESS,
611
        default=NULL,
612
    )
613
    setup_type_group.add_argument(
614
        "--no-user",
615
        action="store_false",
616
        # help="Don't initialize conda for the current user (default).",
617
        help=SUPPRESS,
618
        default=NULL,
619
    )
620
    setup_type_group.add_argument(
621
        "--system",
622
        action="store_true",
623
        # help="Initialize conda for all users on the system.",
624
        help=SUPPRESS,
625
        default=NULL,
626
    )
627
628
    p.add_argument(
629
        'shells',
630
        nargs='*',
631
        help="One or more shells to be initialized. If not given, the default value is "
632
             "'bash' on unix and 'cmd.exe' on Windows. Use the '--all' flag to initialize "
633
             "all shells. Currently compatible shells are {%s}"
634
             % ", ".join(sorted(COMPATIBLE_SHELLS)),
635
    )
636
637
    if on_win:
638
        p.add_argument(
639
            "--anaconda-prompt",
640
            action="store_true",
641
            help="Add an 'Anaconda Prompt' icon to your desktop.",
642
            default=NULL,
643
        )
644
645
    add_parser_json(p)
646
    p.add_argument(
647
        "--dry-run",
648
        action="store_true",
649
        help="Only display what would have been done.",
650
    )
651
    p.set_defaults(func='.main_init.execute')
652
653
654
def configure_parser_help(sub_parsers):
655
    descr = "Displays a list of available conda commands and their help strings."
656
657
    p = sub_parsers.add_parser(
658
        'help',
659
        description=descr,
660
        help=descr,
661
    )
662
    p.add_argument(
663
        'command',
664
        metavar='COMMAND',
665
        action="store",
666
        nargs='?',
667
        help="Print help information for COMMAND (same as: conda COMMAND --help).",
668
    )
669
    p.set_defaults(func='.main_help.execute')
670
671
672
def configure_parser_install(sub_parsers):
673
    help = "Installs a list of packages into a specified conda environment."
674
    descr = dedent(help + """
675
676
    This command accepts a list of package specifications (e.g, bitarray=0.8)
677
    and installs a set of packages consistent with those specifications and
678 View Code Duplication
    compatible with the underlying environment. If full compatibility cannot
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
679
    be assured, an error is reported and the environment is not changed.
680
681
    Conda attempts to install the newest versions of the requested packages. To
682
    accomplish this, it may update some packages that are already installed, or
683
    install additional packages. To prevent existing packages from updating,
684
    use the --freeze-installed option. This may force conda to install older
685
    versions of the requested packages, and it does not prevent additional
686
    dependency packages from being installed.
687
688
    If you wish to skip dependency checking altogether, use the '--no-deps'
689
    option. This may result in an environment with incompatible packages, so
690
    this option must be used with great caution.
691
692
    conda can also be called with a list of explicit conda package filenames
693
    (e.g. ./lxml-3.2.0-py27_0.tar.bz2). Using conda in this mode implies the
694
    --no-deps option, and should likewise be used with great caution. Explicit
695
    filenames and package specifications cannot be mixed in a single command.
696
    """)
697
    example = dedent("""
698
    Examples:
699
700
        conda install -n myenv scipy
701
702
    """)
703
    p = sub_parsers.add_parser(
704
        'install',
705
        description=descr,
706
        help=help,
707
        epilog=example,
708
    )
709
    p.add_argument(
710
        "--revision",
711
        action="store",
712
        help="Revert to the specified REVISION.",
713
        metavar='REVISION',
714
    )
715
716
    solver_mode_options, package_install_options = add_parser_create_install_update(p)
717
718
    add_parser_prune(solver_mode_options)
719
    solver_mode_options.add_argument(
720
        "--force-reinstall",
721
        action="store_true",
722
        default=NULL,
723
        help="Ensure that any user-requested package for the current operation is uninstalled and "
724
             "reinstalled, even if that package already exists in the environment.",
725
    )
726
    add_parser_update_modifiers(solver_mode_options)
727
    package_install_options.add_argument(
728
        '-m', "--mkdir",
729
        action="store_true",
730
        help="Create the environment directory if necessary.",
731
    )
732
    package_install_options.add_argument(
733
        "--clobber",
734
        action="store_true",
735
        default=NULL,
736
        help="Allow clobbering of overlapping file paths within packages, "
737
             "and suppress related warnings.",
738
    )
739
    p.set_defaults(func='.main_install.execute')
740
741
742
def configure_parser_list(sub_parsers):
743
    descr = "List linked packages in a conda environment."
744
745
    # Note, the formatting of this is designed to work well with help2man
746
    examples = dedent("""
747
    Examples:
748
749
    List all packages in the current environment:
750
751
        conda list
752
753
    List all packages installed into the environment 'myenv':
754
755
        conda list -n myenv
756
757
    Save packages for future use:
758
759
        conda list --export > package-list.txt
760
761
    Reinstall packages from an export file:
762
763
        conda create -n myenv --file package-list.txt
764
765
    """)
766
    p = sub_parsers.add_parser(
767
        'list',
768
        description=descr,
769
        help=descr,
770
        formatter_class=RawDescriptionHelpFormatter,
771
        epilog=examples,
772
        add_help=False,
773
    )
774
    add_parser_help(p)
775
    add_parser_prefix(p)
776
    add_parser_json(p)
777
    add_parser_show_channel_urls(p)
778
    p.add_argument(
779
        '-c', "--canonical",
780
        action="store_true",
781
        help="Output canonical names of packages only. Implies --no-pip. ",
782
    )
783
    p.add_argument(
784
        '-f', "--full-name",
785
        action="store_true",
786
        help="Only search for full names, i.e., ^<regex>$.",
787
    )
788
    p.add_argument(
789
        "--explicit",
790
        action="store_true",
791
        help="List explicitly all installed conda packaged with URL "
792
             "(output may be used by conda create --file).",
793
    )
794
    p.add_argument(
795
        "--md5",
796
        action="store_true",
797
        help="Add MD5 hashsum when using --explicit",
798
    )
799
    p.add_argument(
800
        '-e', "--export",
801
        action="store_true",
802
        help="Output requirement string only (output may be used by "
803
             " conda create --file).",
804
    )
805
    p.add_argument(
806
        '-r', "--revisions",
807
        action="store_true",
808
        help="List the revision history and exit.",
809
    )
810
    p.add_argument(
811
        "--no-pip",
812
        action="store_false",
813
        default=True,
814
        dest="pip",
815
        help="Do not include pip-only installed packages.")
816
    p.add_argument(
817
        'regex',
818
        action="store",
819
        nargs="?",
820
        help="List only packages matching this regular expression.",
821
    )
822
    p.set_defaults(func='.main_list.execute')
823
824
825
def configure_parser_package(sub_parsers):
826
    descr = "Low-level conda package utility. (EXPERIMENTAL)"
827
    p = sub_parsers.add_parser(
828
        'package',
829
        description=descr,
830
        help=descr,
831 View Code Duplication
    )
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
832
    add_parser_prefix(p)
833
    p.add_argument(
834
        '-w', "--which",
835
        metavar="PATH",
836
        nargs='+',
837
        action="store",
838
        help="Given some PATH print which conda package the file came from.",
839
    )
840
    p.add_argument(
841
        '-r', "--reset",
842
        action="store_true",
843
        help="Remove all untracked files and exit.",
844
    )
845
    p.add_argument(
846
        '-u', "--untracked",
847
        action="store_true",
848
        help="Display all untracked files and exit.",
849
    )
850
    p.add_argument(
851
        "--pkg-name",
852
        action="store",
853
        default="unknown",
854
        help="Package name of the created package.",
855
    )
856
    p.add_argument(
857
        "--pkg-version",
858
        action="store",
859
        default="0.0",
860
        help="Package version of the created package.",
861
    )
862
    p.add_argument(
863
        "--pkg-build",
864
        action="store",
865
        default=0,
866
        help="Package build number of the created package.",
867
    )
868
    p.set_defaults(func='.main_package.execute')
869
870
871
def configure_parser_remove(sub_parsers, name='remove'):
872
    help = "%s a list of packages from a specified conda environment."
873
    descr = dedent(help + """
874
875
    This command will also remove any package that depends on any of the
876
    specified packages as well---unless a replacement can be found without
877
    that dependency. If you wish to skip this dependency checking and remove
878
    just the requested packages, add the '--force' option. Note however that
879
    this may result in a broken environment, so use this with caution.
880
    """)
881
    example = dedent("""
882
    Examples:
883
884
        conda %s -n myenv scipy
885
886
    """)
887
888
    uninstall_help = "Alias for conda remove."
889
    if name == 'remove':
890
        p = sub_parsers.add_parser(
891
            name,
892
            formatter_class=RawDescriptionHelpFormatter,
893
            description=descr % name.capitalize(),
894
            help=help % name.capitalize(),
895
            epilog=example % name,
896
            add_help=False,
897
        )
898
    else:
899
        p = sub_parsers.add_parser(
900
            name,
901
            formatter_class=RawDescriptionHelpFormatter,
902
            description=uninstall_help,
903
            help=uninstall_help,
904
            epilog=example % name,
905
            add_help=False,
906
        )
907
    add_parser_help(p)
908
    add_parser_pscheck(p)
909
910
    add_parser_prefix(p)
911
    add_parser_channels(p)
912
913
    solver_mode_options = p.add_argument_group("Solver Mode Modifiers")
914
    solver_mode_options.add_argument(
915
        "--all",
916
        action="store_true",
917
        help="%s all packages, i.e., the entire environment." % name.capitalize(),
918
    )
919
    solver_mode_options.add_argument(
920
        "--features",
921
        action="store_true",
922
        help="%s features (instead of packages)." % name.capitalize(),
923
    )
924
    solver_mode_options.add_argument(
925
        "--force-remove", "--force",
926
        action="store_true",
927
        help="Forces removal of a package without removing packages that depend on it. "
928
             "Using this option will usually leave your environment in a broken and "
929
             "inconsistent state.",
930
        dest='force_remove',
931
    )
932
    solver_mode_options.add_argument(
933
        "--no-pin",
934
        action="store_true",
935
        dest='ignore_pinned',
936
        default=NULL,
937
        help="Ignore pinned file.",
938
    )
939
    add_parser_prune(solver_mode_options)
940
941
    add_parser_networking(p)
942
    add_output_and_prompt_options(p)
943
944
    p.add_argument(
945
        'package_names',
946
        metavar='package_name',
947
        action="store",
948
        nargs='*',
949
        help="Package names to %s from the environment." % name,
950
    )
951
952
    p.set_defaults(func='.main_remove.execute')
953
954
955 View Code Duplication
def configure_parser_run(sub_parsers):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
956
    help = "Run an executable in a conda environment. [Experimental]"
957
    descr = help + dedent("""
958
959
    Use '--' (double dash) to separate CLI flags for 'conda run' from CLI flags sent to
960
    the process being launched.
961
962
    Example usage:
963
964
        $ conda create -y -n my-python-2-env python=2
965
        $ conda run -n my-python-2-env python -- --version
966
    """)
967
968
    epilog = dedent("""
969
    """)
970
971
    p = sub_parsers.add_parser(
972
        'run',
973
        description=descr,
974
        help=help,
975
        epilog=epilog,
976
    )
977
978
    add_parser_prefix(p)
979
    p.add_argument(
980
        "-v", "--verbose",
981
        action=NullCountAction,
982
        help="Use once for info, twice for debug, three times for trace.",
983
        dest="verbosity",
984
        default=NULL,
985
    )
986
987
    p.add_argument(
988
        'executable_call',
989
        nargs=REMAINDER,
990
        help="Executable name, with additional arguments to be passed to the executable "
991
             "on invocation.",
992
    )
993
    p.set_defaults(func='.main_run.execute')
994
995
996
def configure_parser_search(sub_parsers):
997
    descr = dedent("""Search for packages and display associated information.
998
    The input is a MatchSpec, a query language for conda packages.
999
    See examples below.
1000
    """)
1001
1002
    example = dedent("""
1003
    Examples:
1004
1005
    Search for a specific package named 'scikit-learn':
1006
1007
        conda search scikit-learn
1008
1009
    Search for packages containing 'scikit' in the package name:
1010
1011
        conda search *scikit*
1012
1013
    Note that your shell may expand '*' before handing the command over to conda.
1014
    Therefore it is sometimes necessary to use single or double quotes around the query.
1015
1016
        conda search '*scikit'
1017
        conda search "*scikit*"
1018
1019
    Search for packages for 64-bit Linux (by default, packages for your current
1020
    platform are shown):
1021
1022
        conda search numpy[subdir=linux-64]
1023
1024
    Search for a specific version of a package:
1025
1026
        conda search 'numpy>=1.12'
1027
1028
    Search for a package on a specific channel
1029
1030
        conda search conda-forge::numpy
1031
        conda search 'numpy[channel=conda-forge, subdir=osx-64]'
1032
    """)
1033
    p = sub_parsers.add_parser(
1034
        'search',
1035
        description=descr,
1036
        help=descr,
1037
        epilog=example,
1038
    )
1039
    p.add_argument(
1040
        "--envs",
1041
        action="store_true",
1042
        help="Search all of the current user's environments. If run as Administrator "
1043
             "(on Windows) or UID 0 (on unix), search all known environments on the system.",
1044
    )
1045
    p.add_argument(
1046
        '-i', "--info",
1047
        action="store_true",
1048
        help="Provide detailed information about each package."
1049
    )
1050
    p.add_argument(
1051
        '--subdir', '--platform',
1052
        action='store',
1053
        dest='subdir',
1054
        help="Search the given subdir. Should be formatted like 'osx-64', 'linux-32', "
1055
             "'win-64', and so on. The default is to search the current platform.",
1056
        default=NULL,
1057
    )
1058
    p.add_argument(
1059
        'match_spec',
1060
        default='*',
1061
        nargs='?',
1062
        help=SUPPRESS,
1063
    )
1064
1065
    p.add_argument(
1066
        "--canonical",
1067
        action="store_true",
1068
        help=SUPPRESS,
1069
    )
1070
    p.add_argument(
1071
        '-f', "--full-name",
1072
        action="store_true",
1073
        help=SUPPRESS,
1074
    )
1075
    p.add_argument(
1076
        "--names-only",
1077
        action="store_true",
1078
        help=SUPPRESS,
1079
    )
1080
    add_parser_known(p)
1081
    p.add_argument(
1082
        '-o', "--outdated",
1083
        action="store_true",
1084
        help=SUPPRESS,
1085
    )
1086
    p.add_argument(
1087
        "--spec",
1088
        action="store_true",
1089
        help=SUPPRESS,
1090
    )
1091
    p.add_argument(
1092
        "--reverse-dependency",
1093
        action="store_true",
1094
        # help="Perform a reverse dependency search. Use 'conda search package --info' "
1095
        #      "to see the dependencies of a package.",
1096
        help=SUPPRESS,  # TODO: re-enable once we have --reverse-dependency working again
1097
    )
1098
1099
    add_parser_channels(p)
1100
    add_parser_networking(p)
1101
    add_parser_json(p)
1102
    p.set_defaults(func='.main_search.execute')
1103
1104
1105
def configure_parser_update(sub_parsers, name='update'):
1106
    help = "Updates conda packages to the latest compatible version."
1107
    descr = dedent(help + """
1108
1109
    This command accepts a list of package names and updates them to the latest
1110
    versions that are compatible with all other packages in the environment.
1111
1112
    Conda attempts to install the newest versions of the requested packages. To
1113
    accomplish this, it may update some packages that are already installed, or
1114
    install additional packages. To prevent existing packages from updating,
1115
    use the --no-update-deps option. This may force conda to install older
1116
    versions of the requested packages, and it does not prevent additional
1117
    dependency packages from being installed.
1118
    """)
1119
    example = dedent("""
1120
    Examples:
1121
1122
        conda %s -n myenv scipy
1123
1124
    """)
1125
1126
    alias_help = "Alias for conda update."
1127
    if name == 'update':
1128
        p = sub_parsers.add_parser(
1129
            'update',
1130
            description=descr,
1131
            help=help,
1132
            epilog=example % name,
1133
        )
1134
    else:
1135
        p = sub_parsers.add_parser(
1136
            name,
1137
            description=alias_help,
1138
            help=alias_help,
1139
            epilog=example % name,
1140
        )
1141
    solver_mode_options, package_install_options = add_parser_create_install_update(p)
1142
1143
    add_parser_prune(solver_mode_options)
1144
    solver_mode_options.add_argument(
1145
        "--force-reinstall",
1146
        action="store_true",
1147
        default=NULL,
1148
        help="Ensure that any user-requested package for the current operation is uninstalled and "
1149
             "reinstalled, even if that package already exists in the environment.",
1150
    )
1151
    add_parser_update_modifiers(solver_mode_options)
1152
1153
    package_install_options.add_argument(
1154
        "--clobber",
1155
        action="store_true",
1156
        default=NULL,
1157
        help="Allow clobbering of overlapping file paths within packages, "
1158
             "and suppress related warnings.",
1159
    )
1160
    p.set_defaults(func='.main_update.execute')
1161
1162
1163
# #############################################################################################
1164
#
1165
# parser helpers
1166
#
1167
# #############################################################################################
1168
1169
def add_parser_create_install_update(p):
1170
    add_parser_prefix(p)
1171
    add_parser_channels(p)
1172
    solver_mode_options = add_parser_solver_mode(p)
1173
    package_install_options = add_parser_package_install_options(p)
1174
    add_parser_networking(p)
1175
1176
    output_and_prompt_options = add_output_and_prompt_options(p)
1177
    output_and_prompt_options.add_argument(
1178
        "--download-only",
1179
        action="store_true",
1180
        default=NULL,
1181
        help="Solve an environment and ensure package caches are populated, but exit "
1182
             "prior to unlinking and linking packages into the prefix.",
1183
    )
1184
    add_parser_show_channel_urls(output_and_prompt_options)
1185
1186
    add_parser_pscheck(p)
1187
    add_parser_known(p)
1188
1189
    # Add the file kwarg. We don't use {action="store", nargs='*'} as we don't
1190
    # want to gobble up all arguments after --file.
1191
    p.add_argument(
1192
        "--file",
1193
        default=[],
1194
        action='append',
1195
        help="Read package versions from the given file. Repeated file "
1196
             "specifications can be passed (e.g. --file=file1 --file=file2).",
1197
    )
1198
    p.add_argument(
1199
        'packages',
1200
        metavar='package_spec',
1201
        action="store",
1202
        nargs='*',
1203
        help="Packages to install or update in the conda environment.",
1204
    )
1205
1206
    return solver_mode_options, package_install_options
1207
1208
1209
def add_parser_pscheck(p):
1210
    p.add_argument(
1211
        "--force-pscheck",
1212
        action="store_true",
1213
        help=SUPPRESS
1214
    )
1215
1216
1217
def add_parser_show_channel_urls(p):
1218
    p.add_argument(
1219
        "--show-channel-urls",
1220
        action="store_true",
1221
        dest="show_channel_urls",
1222
        default=NULL,
1223
        help="Show channel urls. "
1224
             "Overrides the value given by `conda config --show show_channel_urls`.",
1225
    )
1226
    p.add_argument(
1227
        "--no-show-channel-urls",
1228
        action="store_false",
1229
        dest="show_channel_urls",
1230
        help=SUPPRESS,
1231
    )
1232
1233
1234
def add_parser_help(p):
1235
    """
1236
    So we can use consistent capitalization and periods in the help. You must
1237
    use the add_help=False argument to ArgumentParser or add_parser to use
1238
    this. Add this first to be consistent with the default argparse output.
1239
1240
    """
1241
    p.add_argument(
1242
        '-h', '--help',
1243
        action=_HelpAction,
1244
        help="Show this help message and exit.",
1245
    )
1246
1247
1248
def add_parser_prefix(p):
1249
    target_environment_group = p.add_argument_group("Target Environment Specification")
1250
    npgroup = target_environment_group.add_mutually_exclusive_group()
1251
    npgroup.add_argument(
1252
        '-n', "--name",
1253
        action="store",
1254
        help="Name of environment.",
1255
        metavar="ENVIRONMENT",
1256
    )
1257
    npgroup.add_argument(
1258
        '-p', "--prefix",
1259
        action="store",
1260
        help="Full path to environment location (i.e. prefix).",
1261
        metavar='PATH',
1262
    )
1263
1264
1265
def add_parser_json(p):
1266
    output_and_prompt_options = p.add_argument_group("Output, Prompt, and Flow Control Options")
1267
    output_and_prompt_options.add_argument(
1268
        "--debug",
1269
        action="store_true",
1270
        default=NULL,
1271
        help=SUPPRESS,
1272
    )
1273
    output_and_prompt_options.add_argument(
1274
        "--json",
1275
        action="store_true",
1276
        default=NULL,
1277
        help="Report all output as json. Suitable for using conda programmatically."
1278
    )
1279
    output_and_prompt_options.add_argument(
1280
        "-v", "--verbose",
1281
        action=NullCountAction,
1282
        help="Use once for info, twice for debug, three times for trace.",
1283
        dest="verbosity",
1284
        default=NULL,
1285
    )
1286
    output_and_prompt_options.add_argument(
1287
        '-q', "--quiet",
1288
        action="store_true",
1289
        default=NULL,
1290
        help="Do not display progress bar.",
1291
    )
1292
    return output_and_prompt_options
1293
1294
1295
def add_output_and_prompt_options(p):
1296
    output_and_prompt_options = p.add_argument_group("Output, Prompt, and Flow Control Options")
1297
    output_and_prompt_options.add_argument(
1298
        "--debug",
1299
        action="store_true",
1300
        default=NULL,
1301
        help=SUPPRESS,
1302
    )
1303
    output_and_prompt_options.add_argument(
1304
        "--dry-run",
1305
        action="store_true",
1306
        help="Only display what would have been done.",
1307
    )
1308
    output_and_prompt_options.add_argument(
1309
        "--json",
1310
        action="store_true",
1311
        default=NULL,
1312
        help="Report all output as json. Suitable for using conda programmatically."
1313
    )
1314
    output_and_prompt_options.add_argument(
1315
        '-q', "--quiet",
1316
        action="store_true",
1317
        default=NULL,
1318
        help="Do not display progress bar.",
1319
    )
1320
    output_and_prompt_options.add_argument(
1321
        "-v", "--verbose",
1322
        action=NullCountAction,
1323
        help="Can be used multiple times. Once for INFO, twice for DEBUG, three times for TRACE.",
1324
        dest="verbosity",
1325
        default=NULL,
1326
    )
1327
    output_and_prompt_options.add_argument(
1328
        "-y", "--yes",
1329
        action="store_true",
1330
        default=NULL,
1331
        help="Do not ask for confirmation.",
1332
    )
1333
    return output_and_prompt_options
1334
1335
1336
def add_parser_channels(p):
1337
    channel_customization_options = p.add_argument_group("Channel Customization")
1338
    channel_customization_options.add_argument(
1339
        '-c', '--channel',
1340
        dest='channel',  # apparently conda-build uses this; someday rename to channels are remove context.channels alias to channel  # NOQA
1341
        # TODO: if you ever change 'channel' to 'channels', make sure you modify the context.channels property accordingly # NOQA
1342
        action="append",
1343
        help="""Additional channel to search for packages. These are URLs searched in the order
1344
        they are given (including file:// for local directories).  Then, the defaults
1345
        or channels from .condarc are searched (unless --override-channels is given).  You can use
1346
        'defaults' to get the default packages for conda.  You can also use any name and the
1347
        .condarc channel_alias value will be prepended.  The default channel_alias
1348
        is http://conda.anaconda.org/.""",
1349
    )
1350
    channel_customization_options.add_argument(
1351
        "--use-local",
1352
        action="store_true",
1353
        default=NULL,
1354
        help="Use locally built packages. Identical to '-c local'.",
1355
    )
1356
    channel_customization_options.add_argument(
1357
        "--override-channels",
1358
        action="store_true",
1359
        help="""Do not search default or .condarc channels.  Requires --channel.""",
1360
    )
1361
    return channel_customization_options
1362
1363
1364
def add_parser_solver_mode(p):
1365
    solver_mode_options = p.add_argument_group("Solver Mode Modifiers")
1366
    deps_modifiers = solver_mode_options.add_mutually_exclusive_group()
1367
    solver_mode_options.add_argument(
1368
        "--channel-priority",
1369
        action="store_true",
1370
        dest="channel_priority",
1371
        default=NULL,
1372
        help=SUPPRESS,
1373
    )
1374
    solver_mode_options.add_argument(
1375
        "--no-channel-priority",
1376
        action="store_false",
1377
        dest="channel_priority",
1378
        default=NULL,
1379
        help="Package version takes precedence over channel priority. "
1380
             "Overrides the value given by `conda config --show channel_priority`."
1381
    )
1382
    deps_modifiers.add_argument(
1383
        "--no-deps",
1384
        action="store_const",
1385
        const=DepsModifier.NO_DEPS,
1386
        dest="deps_modifier",
1387
        help="Do not install, update, remove, or change dependencies. This WILL lead "
1388
             "to broken environments and inconsistent behavior. Use at your own risk.",
1389
        default=NULL,
1390
    )
1391
    deps_modifiers.add_argument(
1392
        "--only-deps",
1393
        action="store_const",
1394
        const=DepsModifier.ONLY_DEPS,
1395
        dest="deps_modifier",
1396
        help="Only install dependencies.",
1397
        default=NULL,
1398
    )
1399
    solver_mode_options.add_argument(
1400
        "--no-pin",
1401
        action="store_true",
1402
        dest='ignore_pinned',
1403
        default=NULL,
1404
        help="Ignore pinned file.",
1405
    )
1406
    return solver_mode_options
1407
1408
1409
def add_parser_update_modifiers(solver_mode_options):
1410
    update_modifiers = solver_mode_options.add_mutually_exclusive_group()
1411
    update_modifiers.add_argument(
1412
        "--freeze-installed", "--no-update-deps",
1413
        action="store_const",
1414
        const=UpdateModifier.FREEZE_INSTALLED,
1415
        dest="update_modifier",
1416
        default=NULL,
1417
        help="Do not update or change already-installed dependencies.",
1418
    )
1419
    update_modifiers.add_argument(
1420
        "--update-deps",
1421
        action="store_const",
1422
        const=UpdateModifier.UPDATE_DEPS,
1423
        dest="update_modifier",
1424
        default=NULL,
1425
        help="Update dependencies.",
1426
    )
1427
    update_modifiers.add_argument(
1428
        "-S", "--satisfied-skip-solve",
1429
        action="store_const",
1430
        const=UpdateModifier.SPECS_SATISFIED_SKIP_SOLVE,
1431
        dest="update_modifier",
1432
        default=NULL,
1433
        help="Exit early and do not run the solver if the requested specs are satisfied. "
1434
             "Also skips aggressive updates as configured by 'aggressive_update_packages'. "
1435
             "Similar to the default behavior of 'pip install'.",
1436
    )
1437
    update_modifiers.add_argument(
1438
        "--update-all", "--all",
1439
        action="store_const",
1440
        const=UpdateModifier.UPDATE_ALL,
1441
        dest="update_modifier",
1442
        help="Update all installed packages in the environment.",
1443
        default=NULL,
1444
    )
1445
1446
1447
def add_parser_prune(p):
1448
    p.add_argument(
1449
        "--prune",
1450
        action="store_true",
1451
        default=NULL,
1452
        help="Remove packages that have previously been brought into the environment to satisfy "
1453
             "dependencies of user-requested packages, but are no longer needed.",
1454
    )
1455
1456
1457
def add_parser_networking(p):
1458
    networking_options = p.add_argument_group("Networking Options")
1459
    networking_options.add_argument(
1460
        "-C", "--use-index-cache",
1461
        action="store_true",
1462
        default=False,
1463
        help="Use cache of channel index files, even if it has expired.",
1464
    )
1465
    networking_options.add_argument(
1466
        "-k", "--insecure",
1467
        action="store_false",
1468
        dest="ssl_verify",
1469
        default=NULL,
1470
        help="Allow conda to perform \"insecure\" SSL connections and transfers. "
1471
             "Equivalent to setting 'ssl_verify' to 'false'."
1472
    )
1473
    networking_options.add_argument(
1474
        "--offline",
1475
        action='store_true',
1476
        default=NULL,
1477
        help="Offline mode. Don't connect to the Internet.",
1478
    )
1479
    return networking_options
1480
1481
1482
def add_parser_package_install_options(p):
1483
    package_install_options = p.add_argument_group("Package Linking and Install-time Options")
1484
    package_install_options.add_argument(
1485
        '-f', "--force",
1486
        action="store_true",
1487
        default=NULL,
1488
        help=SUPPRESS,
1489
    )
1490
    package_install_options.add_argument(
1491
        '--copy',
1492
        action="store_true",
1493
        default=NULL,
1494
        help="Install all packages using copies instead of hard- or soft-linking."
1495
    )
1496
    if on_win:
1497
        package_install_options.add_argument(
1498
            "--shortcuts",
1499
            action="store_true",
1500
            help=SUPPRESS,
1501
            dest="shortcuts",
1502
            default=NULL,
1503
        )
1504
        package_install_options.add_argument(
1505
            "--no-shortcuts",
1506
            action="store_false",
1507
            help="Don't install start menu shortcuts",
1508
            dest="shortcuts",
1509
            default=NULL,
1510
        )
1511
    return package_install_options
1512
1513
1514
def add_parser_known(p):
1515
    p.add_argument(
1516
        "--unknown",
1517
        action="store_true",
1518
        default=False,
1519
        dest='unknown',
1520
        help=SUPPRESS,
1521
    )
1522