Beispiel #1
0
def conda(location, force):  # no cov
    if not force and conda_available():
        echo_warning(('Conda is already in PATH! If you are sure you want '
                      'to proceed, try again with the -f/--force flag.'))
        sys.exit(2)
    else:
        if ON_WINDOWS:
            installer_name = 'installer.exe'
            if is_os_64bit():
                url = 'https://repo.continuum.io/miniconda/Miniconda3-latest-Windows-x86_64.exe'
            else:
                url = 'https://repo.continuum.io/miniconda/Miniconda3-latest-Windows-x86.exe'
        else:
            installer_name = 'installer.sh'
            if ON_MACOS:
                if is_os_64bit():
                    url = 'https://repo.continuum.io/miniconda/Miniconda3-latest-MacOSX-x86_64.sh'
                else:
                    echo_failure(
                        'Conda is not available for 32-bit macOS, sorry!')
                    sys.exit(1)
            else:
                if is_os_64bit():
                    url = 'https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh'
                else:
                    url = 'https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86.sh'

        with temp_chdir() as d:
            fname = os.path.join(d, installer_name)
            download_file(url, fname)

            location = os.path.abspath(location)
            if os.path.exists(location):
                if force:
                    remove_path(location)
                else:
                    echo_warning((
                        '`{}` already exists! If you are sure you want to proceed, '
                        'try again with the -f/--force flag.'.format(location)
                    ))
                    sys.exit(2)

            if ON_WINDOWS:
                command = [
                    'start', '/wait', '', fname, '/S',
                    '/InstallationType=JustMe', '/AddToPath=0',
                    '/RegisterPython=0', '/D={}'.format(location)
                ]
            else:
                command = ['bash', fname, '-b', '-p', location]

            result = subprocess.run(command, shell=NEED_SUBPROCESS_SHELL)
Beispiel #2
0
def autocomplete():
    """Install tab-autcoomplete for bash terminals.

    \b
    $ hatch autocomplete
    Script installed in ~/.hatchrc and sourced in ~/.bashrc
    """

    if not install_script():
        echo_warning(f"Something went wrong: script not installed")
        sys.exit(1)

    if not add_script_to_bashrc():
        echo_warning(f"Something went wrong: script installed but not added to ~/.bashrc")
        sys.exit(1)

    echo_success(f'Script installed in {HATCHRC_FILE} and sourced in {BASHRC_FILE}.')
    echo_info(f' To activate now, run:\nsource ~/.bashrc\n')
Beispiel #3
0
def init(name, no_env, pyname, pypath, global_packages, env_name, basic, cli,
         licenses):
    """Creates a new Python project in the current directory.

    Values from your config file such as `name` and `pyversions` will be used
    to help populate fields. You can also specify things like the readme format
    and which CI service files to create. All options override the config file.

    By default a virtual env will be created in the project directory and will
    install the project locally so any edits will auto-update the installation.
    You can also locally install the created project in other virtual envs using
    the --env option.

    Here is an example using an unmodified config file:

    \b
    $ hatch init my-app
    Created project `my-app` here
    $ tree --dirsfirst .
    .
    ├── my_app
    │   └── __init__.py
    ├── tests
    │   └── __init__.py
    ├── LICENSE-APACHE
    ├── LICENSE-MIT
    ├── MANIFEST.in
    ├── README.rst
    ├── requirements.txt
    ├── setup.py
    └── tox.ini

    2 directories, 8 files
    """
    try:
        settings = load_settings()
    except FileNotFoundError:
        settings = {}
        echo_warning(
            'Unable to locate config file; try `hatch config --restore`. '
            'The default project structure will be used.')

    if basic:
        settings['basic'] = True

    if licenses:
        settings['licenses'] = licenses.split(',')

    settings['cli'] = cli

    d = os.getcwd()
    create_package(d, name, settings)
    echo_success('Created project `{}` here'.format(name))

    venvs = env_name.split('/') if env_name else []
    if (venvs or not no_env) and pyname:
        try:
            settings = load_settings()
        except FileNotFoundError:  # no cov
            echo_failure(
                'Unable to locate config file. Try `hatch config --restore`.')
            sys.exit(1)

        pypath = settings.get('pypaths', {}).get(pyname, None)
        if not pypath:
            echo_failure(
                'Unable to find a Python path named `{}`.'.format(pyname))
            sys.exit(1)

    if not no_env:
        venv_dir = os.path.join(d, 'venv')
        echo_waiting('Creating its own virtual env... ', nl=False)
        create_venv(venv_dir, pypath=pypath, use_global=global_packages)
        echo_success('complete!')

        with venv(venv_dir):
            echo_waiting('Installing locally in the virtual env... ', nl=False)
            install_packages(['-q', '-e', '.'])
            echo_success('complete!')

    for vname in venvs:
        venv_dir = os.path.join(VENV_DIR, vname)
        if not os.path.exists(venv_dir):
            echo_waiting('Creating virtual env `{}`... '.format(vname),
                         nl=False)
            create_venv(venv_dir, pypath=pypath, use_global=global_packages)
            echo_success('complete!')

        with venv(venv_dir):
            echo_waiting(
                'Installing locally in virtual env `{}`... '.format(vname),
                nl=False)
            install_packages(['-q', '-e', '.'])
            echo_success('complete!')
Beispiel #4
0
def uninstall(packages, no_detect, env_name, global_uninstall, admin, dev, quiet, yes):
    """If the option --env is supplied, the uninstall will be applied using
    that named virtual env. Unless the option --global is selected, the
    uninstall will only affect the current user. Of course, this will have
    no effect if a virtual env is in use. The desired name of the admin
    user can be set with the `_DEFAULT_ADMIN_` environment variable.

    With no packages selected, this will uninstall using a `requirements.txt`
    or a dev version of that in the current directory.

    If no --env is chosen, this will attempt to detect a project and use its
    virtual env before resorting to the default pip. No project detection
    will occur if a virtual env is active.
    """
    if not packages:
        reqs = get_requirements_file(os.getcwd(), dev=dev)
        if not reqs:
            echo_failure('Unable to locate a requirements file.')
            sys.exit(1)

        packages = ['-r', reqs]

    # Windows' `runas` allows only a single argument for the
    # command so we catch this case and turn our command into
    # a string later.
    windows_admin_command = None

    if yes:  # no cov
        packages = ['-y', *packages]

    if env_name:
        venv_dir = os.path.join(VENV_DIR, env_name)
        if not os.path.exists(venv_dir):
            echo_failure('Virtual env named `{}` does not exist.'.format(env_name))
            sys.exit(1)

        with venv(venv_dir):
            command = [get_proper_pip(), 'uninstall', *packages] + (['-q'] if quiet else [])
            echo_waiting('Uninstalling in virtual env `{}`...'.format(env_name))
            result = subprocess.run(command, shell=NEED_SUBPROCESS_SHELL)
    elif not venv_active() and not no_detect and os.path.isfile(os.path.join(os.getcwd(), 'setup.py')):
        venv_dir = os.path.join(os.getcwd(), 'venv')
        if not is_venv(venv_dir):
            echo_info('A project has been detected!')
            echo_waiting('Creating a dedicated virtual env... ', nl=False)
            create_venv(venv_dir)
            echo_success('complete!')

            with venv(venv_dir):
                echo_waiting('Installing this project in the virtual env... ', nl=False)
                install_packages(['-q', '-e', '.'])
                echo_success('complete!')

            echo_warning('New virtual envs have nothing to uninstall, exiting...')
            sys.exit(2)

        with venv(venv_dir):
            command = [get_proper_pip(), 'uninstall', *packages] + (['-q'] if quiet else [])
            echo_waiting('Uninstalling for this project...')
            result = subprocess.run(command, shell=NEED_SUBPROCESS_SHELL)
    else:
        command = [get_proper_pip(), 'uninstall'] + (['-q'] if quiet else [])

        if not venv_active() and global_uninstall:  # no cov
            if not admin:
                if ON_WINDOWS:
                    windows_admin_command = get_admin_command()
                else:
                    command = get_admin_command() + command

        command.extend(packages)

        if windows_admin_command:  # no cov
            command = windows_admin_command + [' '.join(command)]

        echo_waiting('Uninstalling...')
        result = subprocess.run(command, shell=NEED_SUBPROCESS_SHELL)

    sys.exit(result.returncode)
Beispiel #5
0
def new(name, no_env, pyname, pypath, global_packages, env_name, venv_prompt,
        basic, cli, licenses, interactive):
    """Creates a new Python project.

    Values from your config file such as `name` and `pyversions` will be used
    to help populate fields. You can also specify things like the readme format
    and which CI service files to create. All options override the config file.

    By default a virtual env will be created in the project directory and will
    install the project locally so any edits will auto-update the installation.
    You can also locally install the created project in other virtual envs using
    the --env option.

    Here is an example using an unmodified config file:

    \b
    $ hatch new my-app
    Created project `my-app`
    $ tree --dirsfirst my-app
    my-app
    ├── my_app
    │   └── __init__.py
    ├── tests
    │   └── __init__.py
    ├── LICENSE-APACHE
    ├── LICENSE-MIT
    ├── MANIFEST.in
    ├── README.rst
    ├── requirements.txt
    ├── setup.py
    └── tox.ini

    2 directories, 8 files
    """
    try:
        settings = load_settings()
    except FileNotFoundError:
        settings = copy_default_settings()
        echo_warning(
            'Unable to locate config file; try `hatch config --restore`. '
            'The default project structure will be used.')

    origin = os.getcwd()
    package_name = name or click.prompt('Project name')

    d = os.path.join(origin, package_name)
    if os.path.exists(d):
        echo_failure('Directory `{}` already exists.'.format(d))
        sys.exit(1)

    if interactive or not name:
        settings['version'] = click.prompt('Version', default='0.0.1')
        settings['description'] = click.prompt('Description', default='')
        settings['name'] = click.prompt('Author',
                                        default=settings.get('name', ''))
        settings['email'] = click.prompt("Author's email",
                                         default=settings.get('email', ''))
        licenses = click.prompt('License(s)',
                                default=licenses or 'mit,apache2')

    if licenses:
        settings['licenses'] = [str.strip(li) for li in licenses.split(',')]

    if basic:
        settings['basic'] = True

    settings['cli'] = cli

    venvs = env_name.split('/') if env_name else []
    if (venvs or not no_env) and pyname:
        try:
            settings = load_settings()
        except FileNotFoundError:  # no cov
            echo_failure(
                'Unable to locate config file. Try `hatch config --restore`.')
            sys.exit(1)

        pypath = settings.get('pypaths', {}).get(pyname, None)
        if not pypath:
            echo_failure(
                'Unable to find a Python path named `{}`.'.format(pyname))
            sys.exit(1)

    os.makedirs(d)
    with chdir(d, cwd=origin):
        create_package(d, package_name, settings)
        echo_success('Created project `{}`'.format(package_name))

        if not no_env:
            venv_dir = os.path.join(d, get_venv_folder())
            echo_waiting('Creating its own virtual env... ', nl=False)
            create_venv(venv_dir,
                        pypath=pypath,
                        use_global=global_packages,
                        venv_prompt=venv_prompt or package_name)
            echo_success('complete!')

            with venv(venv_dir):
                echo_waiting('Installing locally in the virtual env... ',
                             nl=False)
                install_packages(['-q', '-e', '.'])
                echo_success('complete!')

        for vname in venvs:
            venv_dir = os.path.join(get_venv_dir(), vname)
            if not os.path.exists(venv_dir):
                echo_waiting('Creating virtual env `{}`... '.format(vname),
                             nl=False)
                create_venv(venv_dir,
                            pypath=pypath,
                            use_global=global_packages,
                            venv_prompt=vname)
                echo_success('complete!')

            with venv(venv_dir):
                echo_waiting(
                    'Installing locally in virtual env `{}`... '.format(vname),
                    nl=False)
                install_packages(['-q', '-e', '.'])
                echo_success('complete!')
Beispiel #6
0
def grow(part, package, local, path, pre_token, build_token):
    """Increments a project's version number using semantic versioning.
    Valid choices for the part are `major`, `minor`, `patch` (`fix` alias),
    `pre`, and `build`.

    The path to the project is derived in the following order:

    \b
    1. The optional argument, which should be the name of a package
       that was installed via `hatch install -l` or `pip install -e`.
    2. The --local flag.
    3. The option --path, which can be a relative or absolute path.
    4. The current directory.

    If the path is a file, it will be the target. Otherwise, the path, and
    every top level directory within, will be checked for a `__version__.py`,
    `__about__.py`, and `__init__.py`, in that order. The first encounter of
    a `__version__` variable that also appears to equal a version string will
    be updated. Probable package paths will be given precedence.

    The default tokens for the prerelease and build parts, `rc` and `build`
    respectively, can be altered via the options `--pre` and `--build`, or
    the config entry `semver`.

    \b
    $ git clone -q https://github.com/requests/requests && cd requests
    $ hatch grow build
    Updated /home/ofek/requests/requests/__version__.py
    2.18.4 -> 2.18.4+build.1
    $ hatch grow fix
    Updated /home/ofek/requests/requests/__version__.py
    2.18.4+build.1 -> 2.18.5
    $ hatch grow pre
    Updated /home/ofek/requests/requests/__version__.py
    2.18.5 -> 2.18.5-rc.1
    $ hatch grow minor
    Updated /home/ofek/requests/requests/__version__.py
    2.18.5-rc.1 -> 2.19.0
    $ hatch grow major
    Updated /home/ofek/requests/requests/__version__.py
    2.19.0 -> 3.0.0
    """
    if package:
        echo_waiting('Locating package...')
        path = get_editable_package_location(package)
        if not path:
            echo_failure('`{}` is not an editable package.'.format(package))
            sys.exit(1)
    elif local:
        echo_waiting('Locating package...')
        name, path = get_editable_package_location()
        if not name:
            if path is None:
                echo_failure('There are no local packages available.')
                sys.exit(1)
            else:
                echo_failure(
                    'There are multiple local packages available. Select '
                    'one with the optional argument.')
                sys.exit(1)
        echo_info('Package `{}` has been selected.'.format(name))
    elif path:
        possible_path = resolve_path(path)
        if not possible_path:
            echo_failure('Directory `{}` does not exist.'.format(path))
            sys.exit(1)
        path = possible_path
    else:
        path = os.getcwd()

    settings = load_settings(lazy=True)
    pre_token = pre_token or settings.get('semver', {}).get('pre')
    build_token = build_token or settings.get('semver', {}).get('build')

    f, old_version, new_version = bump_package_version(path, part, pre_token,
                                                       build_token)

    if new_version:
        echo_success('Updated {}'.format(f))
        echo_success('{} -> {}'.format(old_version, new_version))
    else:
        if f:
            echo_failure('Found version files:')
            for file in f:
                echo_warning(file)
                echo_failure('\nUnable to find a version specifier.')
            sys.exit(1)
        else:
            echo_failure('No version files found.')
            sys.exit(1)
Beispiel #7
0
def conda(location, force, head, install_only, show):  # no cov
    """Installs Miniconda https://conda.io/docs/glossary.html#miniconda-glossary"""
    location = userpath.normpath(location)
    exe_dir = locate_exe_dir(location, check=False)

    if ON_WINDOWS:
        new_path = os.pathsep.join(
            (location, exe_dir, os.path.join(location, 'Library', 'bin')))
    else:
        new_path = exe_dir

    if show:
        echo_info(new_path)
        return

    if os.path.exists(location) and not force:
        echo_warning(
            ('`{}` already exists! If you are sure you want to proceed, '
             'try again with the -f/--force flag.'.format(location)))
        sys.exit(2)

    if not install_only and not force and conda_available():
        echo_warning(('Conda is already in PATH! If you are sure you want '
                      'to proceed, try again with the -f/--force flag.'))
        sys.exit(2)
    else:
        if ON_WINDOWS:
            installer_name = 'installer.exe'
            if is_os_64bit():
                url = 'https://repo.continuum.io/miniconda/Miniconda3-latest-Windows-x86_64.exe'
            else:
                url = 'https://repo.continuum.io/miniconda/Miniconda3-latest-Windows-x86.exe'
        else:
            installer_name = 'installer.sh'
            if ON_MACOS:
                if is_os_64bit():
                    url = 'https://repo.continuum.io/miniconda/Miniconda3-latest-MacOSX-x86_64.sh'
                else:
                    echo_failure(
                        'Conda is not available for 32-bit macOS, sorry!')
                    sys.exit(1)
            else:
                if is_os_64bit():
                    url = 'https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh'
                else:
                    url = 'https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86.sh'

        with temp_chdir() as d:
            fname = os.path.join(d, installer_name)
            echo_waiting('Downloading installer to a temporary directory... ',
                         nl=False)
            download_file(url, fname)
            echo_success('complete!')

            if ON_WINDOWS:
                command = [
                    'start', '/wait', '', fname, '/S',
                    '/InstallationType=JustMe', '/AddToPath=0',
                    '/RegisterPython=0', '/D={}'.format(location)
                ]
            else:
                command = ['bash', fname, '-b', '-p', location]

            try:
                echo_waiting('Installing, please wait...')
                subprocess.run(command,
                               shell=NEED_SUBPROCESS_SHELL,
                               check=True)
            except subprocess.CalledProcessError:
                echo_failure('Installation has seemingly failed! ', nl=False)
                if force:
                    echo_warning('Proceeding by force...')
                else:
                    echo_failure('Exiting...')
                    sys.exit(1)

    try:
        locate_exe_dir(location)
    except InvalidVirtualEnv:
        echo_failure('Installation has definitely failed! Exiting...')
        sys.exit(1)

    echo_success('Successfully installed Conda!')

    if not install_only:
        add_to_path = userpath.prepend if head else userpath.append
        success = add_to_path(new_path, app_name='Hatch')

        if success:
            echo_info(
                'Please restart your shell for PATH changes to take effect.')
        else:
            echo_warning(
                'It appears that we were unable to modify PATH. Please '
                'do so using the following:',
                nl=False)
            echo_info(new_path)
Beispiel #8
0
def python(version, name, head):  # no cov
    if not conda_available():
        echo_failure(
            'Conda is unavailable. You can install it by doing `hatch conda`.')
        sys.exit(1)

    exe_name = 'py{}'.format(name or version) + ('.exe' if ON_WINDOWS else '')
    name = name or version
    path = os.path.join(get_python_dir(), name)
    command = [
        'conda', 'create', '--yes', '-p', path, 'python={}'.format(version)
    ]

    if os.path.exists(path):
        echo_failure('The path `{}` already exists.'.format(path))
        sys.exit(1)

    settings = load_settings(lazy=True)
    if 'pypaths' not in settings:
        updated_settings = copy_default_settings()
        updated_settings.update(settings)
        settings = updated_settings
        echo_success(
            'Settings were successfully updated to include `pypaths` entry.')

    old_path = settings['pypaths'].get(name)
    if old_path:
        echo_failure('The Python path `{}` already points to `{}`.'.format(
            name, old_path))
        sys.exit(1)

    echo_waiting('Installing Python {}...'.format(version))
    try:
        subprocess.run(command,
                       check=True,
                       stdout=subprocess.PIPE,
                       stderr=subprocess.PIPE)
    except subprocess.CalledProcessError as e:
        echo_failure('The installation was seemingly unsuccessful.')
        click.echo(e.stdout)
        click.echo(e.stderr)
        sys.exit(e.returncode)

    conda_path = get_conda_new_exe_path(path)
    python_path = resolve_path(shutil.which('python', path=conda_path))
    settings['pypaths'][name] = python_path
    save_settings(settings)
    echo_success('Successfully saved Python `{}` located at `{}`.'.format(
        name, python_path))

    if head is not None:
        add_to_path = userpath.prepend if head else userpath.append
        success = add_to_path(conda_path, app_name='Hatch')
        shutil.copy(python_path,
                    os.path.join(os.path.dirname(python_path), exe_name))

        if success:
            echo_info(
                'Please restart your shell for PATH changes to take effect.')
        else:
            echo_warning(
                'It appears that we were unable to modify PATH. Please '
                'do so using the following: ',
                nl=False)
            echo_info(conda_path)
Beispiel #9
0
def shed(ctx, pyname, env_name):
    """Removes named Python paths or virtual environments.

    \b
    $ hatch pypath -l
    py2 -> /usr/bin/python
    py3 -> /usr/bin/python3
    invalid -> :\/:
    $ hatch env -ll
    Virtual environments found in /home/ofek/.virtualenvs:

    \b
    duplicate ->
      Version: 3.5.2
      Implementation: CPython
    fast ->
      Version: 3.5.3
      Implementation: PyPy
    my-app ->
      Version: 3.5.2
      Implementation: CPython
    old ->
      Version: 2.7.12
      Implementation: CPython
    $ hatch shed -p invalid -e duplicate/old
    Successfully removed Python path named `invalid`.
    Successfully removed virtual env named `duplicate`.
    Successfully removed virtual env named `old`.
    """
    if not (pyname or env_name):
        click.echo(ctx.get_help())
        return

    if pyname:
        try:
            settings = load_settings()
        except FileNotFoundError:
            echo_failure(
                'Unable to locate config file. Try `hatch config --restore`.')
            sys.exit(1)

        for pyname in pyname.split('/'):
            pypath = settings.get('pypaths', {}).pop(pyname, None)
            if pypath is not None:
                save_settings(settings)
                echo_success(
                    'Successfully removed Python path named `{}`.'.format(
                        pyname))
            else:
                echo_warning(
                    'Python path named `{}` already does not exist.'.format(
                        pyname))

    if env_name:
        for env_name in env_name.split('/'):
            venv_dir = os.path.join(get_venv_dir(), env_name)
            if os.path.exists(venv_dir):
                remove_path(venv_dir)
                echo_success(
                    'Successfully removed virtual env named `{}`.'.format(
                        env_name))
            else:
                echo_warning(
                    'Virtual env named `{}` already does not exist.'.format(
                        env_name))
Beispiel #10
0
def update(packages, no_detect, env_name, eager, all_packages, infra, global_install,
           admin, force, dev, as_module, self, quiet):
    """If the option --env is supplied, the update will be applied using
    that named virtual env. Unless the option --global is selected, the
    update will only affect the current user. Of course, this will have
    no effect if a virtual env is in use. The desired name of the admin
    user can be set with the `_DEFAULT_ADMIN_` environment variable.

    When performing a global update, your system may use an older version
    of pip that is incompatible with some features such as --eager. To
    force the use of these features, use --force.

    With no packages nor options selected, this will update packages by
    looking for a `requirements.txt` or a dev version of that in the current
    directory.

    If no --env is chosen, this will attempt to detect a project and use its
    virtual env before resorting to the default pip. No project detection
    will occur if a virtual env is active.

    To update this tool, use the --self flag. All other methods of updating will
    ignore `hatch`. See: https://github.com/pypa/pip/issues/1299
    """
    command = ['install', '--upgrade'] + (['-q'] if quiet else [])
    if not global_install or force:  # no cov
        command.extend(['--upgrade-strategy', 'eager' if eager else 'only-if-needed'])

    infra_packages = ['pip', 'setuptools', 'wheel']
    temp_dir = None

    # Windows' `runas` allows only a single argument for the
    # command so we catch this case and turn our command into
    # a string later.
    windows_admin_command = None

    if self:  # no cov
        as_module = True

    if not self and env_name:
        venv_dir = os.path.join(VENV_DIR, env_name)
        if not os.path.exists(venv_dir):
            echo_failure('Virtual env named `{}` does not exist.'.format(env_name))
            sys.exit(1)

        with venv(venv_dir):
            executable = (
                [get_proper_python(), '-m', 'pip']
                if as_module or (infra and ON_WINDOWS)
                else [get_proper_pip()]
            )
            command = executable + command
            if all_packages:
                installed_packages = infra_packages if infra else get_installed_packages()
            else:
                installed_packages = None
    elif not self and not venv_active() and not no_detect and os.path.isfile(os.path.join(os.getcwd(), 'setup.py')):
        venv_dir = os.path.join(os.getcwd(), 'venv')
        if not is_venv(venv_dir):
            echo_info('A project has been detected!')
            echo_waiting('Creating a dedicated virtual env... ', nl=False)
            create_venv(venv_dir)
            echo_success('complete!')

            with venv(venv_dir):
                echo_waiting('Installing this project in the virtual env... ', nl=False)
                install_packages(['-q', '-e', '.'])
                echo_success('complete!')

        with venv(venv_dir):
            executable = (
                [get_proper_python(), '-m', 'pip']
                if as_module or (infra and ON_WINDOWS)
                else [get_proper_pip()]
            )
            command = executable + command
            if all_packages:
                installed_packages = infra_packages if infra else get_installed_packages()
            else:
                installed_packages = None
    else:
        venv_dir = None
        executable = (
            [sys.executable if self else get_proper_python(), '-m', 'pip']
            if as_module or (infra and ON_WINDOWS)
            else [get_proper_pip()]
        )
        command = executable + command
        if all_packages:
            installed_packages = infra_packages if infra else get_installed_packages()
        else:
            installed_packages = None

        if not venv_active():  # no cov
            if global_install:
                if not admin:
                    if ON_WINDOWS:
                        windows_admin_command = get_admin_command()
                    else:
                        command = get_admin_command() + command
            else:
                command.append('--user')

    if self:  # no cov
        command.append('hatch')
        if ON_WINDOWS:
            echo_warning('After the update you may want to press Enter to flush stdout.')
            subprocess.Popen(command, shell=NEED_SUBPROCESS_SHELL)
            sys.exit()
        else:
            result = subprocess.run(command, shell=NEED_SUBPROCESS_SHELL)
            sys.exit(result.returncode)
    elif infra:
        command.extend(infra_packages)
    elif all_packages:
        installed_packages = [
            package for package in installed_packages
            if package not in infra_packages and package != 'hatch'
        ]
        if not installed_packages:
            echo_failure('No packages installed.')
            sys.exit(1)
        command.extend(installed_packages)
    elif packages:
        packages = [package for package in packages if package != 'hatch']
        if not packages:
            echo_failure('No packages to install.')
            sys.exit(1)
        command.extend(packages)

    # When https://github.com/pypa/pipfile is finalized, we'll use it.
    else:
        reqs = get_requirements_file(os.getcwd(), dev=dev)
        if not reqs:
            echo_failure('Unable to locate a requirements file.')
            sys.exit(1)

        with open(reqs, 'r') as f:
            lines = f.readlines()

        matches = []
        for line in lines:
            match = re.match(r'^[^=<>]+', line.lstrip())
            if match and match.group(0) == 'hatch':
                matches.append(line)

        if matches:
            for line in matches:
                lines.remove(line)

            temp_dir = TemporaryDirectory()
            reqs = os.path.join(temp_dir.name, basepath(reqs))

            with open(reqs, 'w') as f:
                f.writelines(lines)

        command.extend(['-r', reqs])

    if windows_admin_command:  # no cov
        command = windows_admin_command + [' '.join(command)]

    if venv_dir:
        with venv(venv_dir):
            if env_name:
                echo_waiting('Updating virtual env `{}`...'.format(env_name))
            else:
                echo_waiting('Updating for this project...')
            result = subprocess.run(command, shell=NEED_SUBPROCESS_SHELL)
    else:
        echo_waiting('Updating...')
        result = subprocess.run(command, shell=NEED_SUBPROCESS_SHELL)

    if temp_dir is not None:
        temp_dir.cleanup()

    sys.exit(result.returncode)