From 6a221cf732e27e847dbbcec769f6adb5f25eed45 Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Fri, 30 Jul 2021 12:31:43 +0800 Subject: [PATCH] Don't share project objects but create own --- pipenv/cli/command.py | 124 ++++++------ pipenv/cli/options.py | 6 +- pipenv/core.py | 218 ++++++++++------------ pipenv/exceptions.py | 11 -- pipenv/help.py | 6 +- pipenv/project.py | 68 +++++-- pipenv/resolver.py | 3 +- pipenv/utils.py | 29 +-- tests/conftest.py | 7 + tests/integration/test_install_markers.py | 1 - tests/unit/test_core.py | 14 +- 11 files changed, 253 insertions(+), 234 deletions(-) diff --git a/pipenv/cli/command.py b/pipenv/cli/command.py index 4387f986..2b2ef274 100644 --- a/pipenv/cli/command.py +++ b/pipenv/cli/command.py @@ -2,22 +2,22 @@ import os import sys from click import ( - argument, echo, edit, group, option, pass_context, secho, types, version_option, Choice + Choice, argument, echo, edit, group, option, pass_context, secho, types, + version_option ) from pipenv.__version__ import __version__ from pipenv._compat import fix_utf8 -from pipenv.exceptions import PipenvOptionsError -from pipenv.patched import crayons -from pipenv.vendor import click_completion -from pipenv.utils import subprocess_run from pipenv.cli.options import ( CONTEXT_SETTINGS, PipenvGroup, code_option, common_options, deploy_option, - general_options, install_options, lock_options, pass_state, + general_options, install_options, lock_options, pass_project, pass_state, pypi_mirror_option, python_option, site_packages_option, skip_lock_option, - sync_options, system_option, three_option, uninstall_options, - verbose_option + sync_options, system_option, three_option, uninstall_options, verbose_option ) +from pipenv.exceptions import PipenvOptionsError +from pipenv.patched import crayons +from pipenv.utils import subprocess_run +from pipenv.vendor import click_completion # Enable shell completion. @@ -54,9 +54,11 @@ subcommand_context_no_interspersion["allow_interspersed_args"] = False @general_options @version_option(prog_name=crayons.normal("pipenv", bold=True), version=__version__) @pass_state +@pass_project @pass_context def cli( ctx, + project, state, where=False, venv=False, @@ -88,15 +90,8 @@ def cli( return 0 from ..core import ( - system_which, - do_py, - warn_in_virtualenv, - do_where, - project, - cleanup_virtualenv, - ensure_project, - format_help, - do_clear, + cleanup_virtualenv, do_clear, do_py, do_where, ensure_project, + format_help, system_which, warn_in_virtualenv ) from ..utils import create_spinner @@ -126,20 +121,20 @@ def cli( if ctx.invoked_subcommand is None: # --where was passed... if where: - do_where(bare=True) + do_where(project, bare=True) return 0 elif py: - do_py() + do_py(project) return 0 # --support was passed... elif support: from ..help import get_pipenv_diagnostics - get_pipenv_diagnostics() + get_pipenv_diagnostics(project) return 0 # --clear was passed... elif state.clear: - do_clear() + do_clear(project) return 0 # --venv was passed... elif venv: @@ -181,7 +176,7 @@ def cli( ) with create_spinner(text="Running..."): # Remove the virtualenv. - cleanup_virtualenv(bare=True) + cleanup_virtualenv(project, bare=True) return 0 else: echo( @@ -195,6 +190,7 @@ def cli( # --two / --three was passed... if (state.python or state.three is not None) or state.site_packages: ensure_project( + project, three=state.three, python=state.python, warn=True, @@ -219,16 +215,19 @@ def cli( @skip_lock_option @install_options @pass_state +@pass_project @pass_context def install( ctx, + project, state, **kwargs ): """Installs provided packages and adds them to Pipfile, or (if no packages are given), installs all packages from Pipfile.""" from ..core import do_install - retcode = do_install( + do_install( + project, dev=state.installstate.dev, three=state.three, python=state.python, @@ -250,8 +249,6 @@ def install( editable_packages=state.installstate.editables, site_packages=state.site_packages ) - if retcode: - ctx.abort() @cli.command( @@ -271,10 +268,12 @@ def install( help="Purge all package(s) from virtualenv. Does not edit Pipfile.", ) @uninstall_options +@pass_project @pass_state @pass_context def uninstall( ctx, + project, state, all_dev=False, all=False, @@ -283,6 +282,7 @@ def uninstall( """Uninstalls a provided package and removes it from Pipfile.""" from ..core import do_uninstall retcode = do_uninstall( + project, packages=state.installstate.packages, editable_packages=state.installstate.editables, three=state.three, @@ -318,19 +318,22 @@ LOCK_DEV_NOTE = """\ @cli.command(short_help="Generates Pipfile.lock.", context_settings=CONTEXT_SETTINGS) @lock_options @pass_state +@pass_project @pass_context def lock( ctx, + project, state, **kwargs ): """Generates Pipfile.lock.""" - from ..core import ensure_project, do_init, do_lock + from ..core import do_init, do_lock, ensure_project + # Ensure that virtualenv is available. # Note that we don't pass clear on to ensure_project as it is also # handled in do_lock ensure_project( - three=state.three, python=state.python, pypi_mirror=state.pypi_mirror, + project, three=state.three, python=state.python, pypi_mirror=state.pypi_mirror, warn=(not state.quiet), site_packages=state.site_packages, ) emit_requirements = state.lockoptions.emit_requirements @@ -352,6 +355,7 @@ def lock( # Setting "emit_requirements=True" means do_init() just emits the # install requirements file to stdout, it doesn't install anything do_init( + project, dev=dev, dev_only=dev_only, emit_requirements=emit_requirements, @@ -365,6 +369,7 @@ def lock( "Aborting." ) do_lock( + project, ctx=ctx, clear=state.clear, pre=pre, @@ -396,14 +401,16 @@ def lock( @three_option @python_option @pass_state +@pass_project def shell( + project, state, fancy=False, shell_args=None, anyway=False, ): """Spawns a shell within the virtualenv.""" - from ..core import load_dot_env, do_shell + from ..core import do_shell, load_dot_env # Prevent user from activating nested environments. if "PIPENV_ACTIVE" in os.environ: @@ -420,11 +427,12 @@ def shell( ) sys.exit(1) # Load .env file. - load_dot_env() + load_dot_env(project) # Use fancy mode for Windows. if os.name == "nt": fancy = True do_shell( + project, three=state.three, python=state.python, fancy=fancy, @@ -441,11 +449,12 @@ def shell( @argument("command") @argument("args", nargs=-1) @pass_state -def run(state, command, args): +@pass_project +def run(project, state, command, args): """Spawns a command installed into the virtualenv.""" from ..core import do_run do_run( - command=command, args=args, three=state.three, python=state.python, pypi_mirror=state.pypi_mirror + project, command=command, args=args, three=state.three, python=state.python, pypi_mirror=state.pypi_mirror ) @@ -495,7 +504,9 @@ def run(state, command, args): @system_option @argument("args", nargs=-1) @pass_state +@pass_project def check( + project, state, unused=False, db=None, @@ -511,6 +522,7 @@ def check( from ..core import do_check do_check( + project, three=state.three, python=state.python, system=state.system, @@ -533,9 +545,11 @@ def check( @option("--dry-run", is_flag=True, default=None, help="List out-of-date dependencies.") @install_options @pass_state +@pass_project @pass_context def update( ctx, + project, state, bare=False, dry_run=None, @@ -543,21 +557,15 @@ def update( **kwargs ): """Runs lock, then sync.""" - from ..core import ( - ensure_project, - do_outdated, - do_lock, - do_sync, - project, - ) + from ..core import do_lock, do_outdated, do_sync, ensure_project, project ensure_project( - three=state.three, python=state.python, pypi_mirror=state.pypi_mirror, + project, three=state.three, python=state.python, pypi_mirror=state.pypi_mirror, warn=(not state.quiet), site_packages=state.site_packages, clear=state.clear ) if not outdated: outdated = bool(dry_run) if outdated: - do_outdated(clear=state.clear, pre=state.installstate.pre, pypi_mirror=state.pypi_mirror) + do_outdated(project, clear=state.clear, pre=state.installstate.pre, pypi_mirror=state.pypi_mirror) packages = [p for p in state.installstate.packages if p] editable = [p for p in state.installstate.editables if p] if not packages: @@ -583,6 +591,7 @@ def update( ) ctx.abort() do_lock( + project, ctx=ctx, clear=state.clear, pre=state.installstate.pre, @@ -591,7 +600,7 @@ def update( write=not state.quiet, ) do_sync( - ctx=ctx, + project, dev=state.installstate.dev, three=state.three, python=state.python, @@ -613,11 +622,12 @@ def update( @option("--json", is_flag=True, default=False, help="Output JSON.") @option("--json-tree", is_flag=True, default=False, help="Output JSON in nested tree.") @option("--reverse", is_flag=True, default=False, help="Reversed dependency graph.") -def graph(bare=False, json=False, json_tree=False, reverse=False): +@pass_project +def graph(project, bare=False, json=False, json_tree=False, reverse=False): """Displays currently-installed dependency graph information.""" from ..core import do_graph - do_graph(bare=bare, json=json, json_tree=json_tree, reverse=reverse) + do_graph(project, bare=bare, json=json, json_tree=json_tree, reverse=reverse) @cli.command( @@ -627,7 +637,8 @@ def graph(bare=False, json=False, json_tree=False, reverse=False): @common_options @argument("module", nargs=1) @pass_state -def run_open(state, module, *args, **kwargs): +@pass_project +def run_open(project, state, module, *args, **kwargs): """View a given module in your editor. This uses the EDITOR environment variable. You can temporarily override it, @@ -635,15 +646,17 @@ def run_open(state, module, *args, **kwargs): EDITOR=atom pipenv open requests """ - from ..core import which, ensure_project, inline_activate_virtual_environment + from ..core import ( + ensure_project, inline_activate_virtual_environment, which + ) # Ensure that virtualenv is available. ensure_project( - three=state.three, python=state.python, + project, three=state.three, python=state.python, validate=False, pypi_mirror=state.pypi_mirror, ) c = subprocess_run( - which("python"), "-c", "import {0}; print({0}.__file__)".format(module) + [which("python"), "-c", "import {0}; print({0}.__file__)".format(module)] ) if c.returncode: echo(crayons.red("Module not found!")) @@ -653,7 +666,7 @@ def run_open(state, module, *args, **kwargs): else: p = c.stdout.strip().rstrip("cdo") echo(crayons.normal(f"Opening {p!r} in your EDITOR.", bold=True)) - inline_activate_virtual_environment() + inline_activate_virtual_environment(project) edit(filename=p) return 0 @@ -666,9 +679,11 @@ def run_open(state, module, *args, **kwargs): @option("--bare", is_flag=True, default=False, help="Minimal output.") @sync_options @pass_state +@pass_project @pass_context def sync( ctx, + project, state, bare=False, user=False, @@ -679,7 +694,7 @@ def sync( from ..core import do_sync retcode = do_sync( - ctx=ctx, + project, dev=state.installstate.dev, three=state.three, python=state.python, @@ -706,11 +721,11 @@ def sync( @three_option @python_option @pass_state -@pass_context -def clean(ctx, state, dry_run=False, bare=False, user=False): +@pass_project +def clean(project, state, dry_run=False, bare=False, user=False): """Uninstalls all packages not specified in Pipfile.lock.""" from ..core import do_clean - do_clean(ctx=ctx, three=state.three, python=state.python, dry_run=dry_run, + do_clean(project, three=state.three, python=state.python, dry_run=dry_run, system=state.system) @@ -719,10 +734,9 @@ def clean(ctx, state, dry_run=False, bare=False, user=False): context_settings=subcommand_context_no_interspersion, ) @common_options -def scripts(): +@pass_project +def scripts(project): """Lists scripts in current environment config.""" - from ..core import project - if not project.pipfile_exists: echo("No Pipfile present at project home.", err=True) sys.exit(1) diff --git a/pipenv/cli/options.py b/pipenv/cli/options.py index 7cde367c..1017bcde 100644 --- a/pipenv/cli/options.py +++ b/pipenv/cli/options.py @@ -3,10 +3,13 @@ import os import click.types from click import ( - BadParameter, BadArgumentUsage, Group, Option, argument, echo, make_pass_decorator, option + BadArgumentUsage, BadParameter, Group, Option, argument, echo, + make_pass_decorator, option ) from click_didyoumean import DYMMixin +from pipenv.project import Project + from .. import environments from ..utils import is_valid_url @@ -89,6 +92,7 @@ class LockOptions: pass_state = make_pass_decorator(State, ensure=True) +pass_project = make_pass_decorator(Project, ensure=True) def index_option(f): diff --git a/pipenv/core.py b/pipenv/core.py index 295881cc..d4dfa108 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -23,10 +23,9 @@ from pipenv.environments import ( SESSION_IS_INTERACTIVE, is_type_checking ) from pipenv.patched import crayons -from pipenv.project import Project from pipenv.utils import ( convert_deps_to_pip, create_spinner, download_file, find_python, - find_windows_executable, get_canonical_names, get_source_list, is_pinned, + get_canonical_names, get_source_list, is_pinned, is_python_command, is_required_version, is_star, is_valid_url, parse_indexes, pep423_name, prepare_pip_source_args, proper_case, python_version, run_command, subprocess_run, venv_resolve_deps @@ -80,37 +79,7 @@ if PIPENV_COLORBLIND: crayons.disable() -def which(command, location=None, allow_global=False): - if not allow_global and location is None: - if project.virtualenv_exists: - location = project.virtualenv_location - else: - location = os.environ.get("VIRTUAL_ENV", None) - if not (location and os.path.exists(location)) and not allow_global: - raise RuntimeError("location not created nor specified") - - version_str = "python{}".format(".".join([str(v) for v in sys.version_info[:2]])) - is_python = command in ("python", os.path.basename(sys.executable), version_str) - if not allow_global: - if os.name == "nt": - p = find_windows_executable(os.path.join(location, "Scripts"), command) - else: - p = os.path.join(location, "bin", command) - else: - if is_python: - p = sys.executable - if not os.path.exists(p): - if is_python: - p = sys.executable or system_which("python") - else: - p = system_which(command) - return p - - -project = Project(which=which) - - -def do_clear(): +def do_clear(project): click.echo(crayons.normal(fix_utf8("Clearing caches..."), bold=True)) try: from pip._internal import locations @@ -134,7 +103,7 @@ def do_clear(): raise -def load_dot_env(): +def load_dot_env(project): """Loads .env file into sys.environ.""" if not environments.PIPENV_DONT_LOAD_ENV: # If the project doesn't exist yet, check current directory for a .env file @@ -163,13 +132,7 @@ def load_dot_env(): importlib.reload(environments) -def add_to_path(p): - """Adds a given path to the PATH.""" - if p not in os.environ["PATH"]: - os.environ["PATH"] = "{}{}{}".format(p, os.pathsep, os.environ["PATH"]) - - -def cleanup_virtualenv(bare=True): +def cleanup_virtualenv(project, bare=True): """Removes the virtualenv directory from the system.""" if not bare: click.echo(crayons.red("Environment creation aborted.")) @@ -187,7 +150,7 @@ def cleanup_virtualenv(bare=True): click.echo(crayons.cyan(e), err=True) -def import_requirements(r=None, dev=False): +def import_requirements(project, r=None, dev=False): from .patched.notpip._vendor import requests as pip_requests from .vendor.pip_shims.shims import parse_requirements @@ -261,12 +224,12 @@ def import_from_code(path="."): return [] -def ensure_pipfile(validate=True, skip_requirements=False, system=False): +def ensure_pipfile(project, validate=True, skip_requirements=False, system=False): """Creates a Pipfile for the project, if it doesn't exist.""" from .environments import PIPENV_VIRTUALENV # Assert Pipfile exists. - python = which("python") if not (USING_DEFAULT_PYTHON or system) else None + python = project._which("python") if not (USING_DEFAULT_PYTHON or system) else None if project.pipfile_is_empty: # Show an error message and exit if system is passed and no pipfile exists if system and not PIPENV_VIRTUALENV: @@ -288,7 +251,7 @@ def ensure_pipfile(validate=True, skip_requirements=False, system=False): with create_spinner("Importing requirements...") as sp: # Import requirements.txt. try: - import_requirements() + import_requirements(project) except Exception: sp.fail(environments.PIPENV_SPINNER_FAIL_TEXT.format("Failed...")) else: @@ -348,7 +311,7 @@ def find_a_system_python(line): return python_entry -def ensure_python(three=None, python=None): +def ensure_python(project, three=None, python=None): # Runtime import is necessary due to the possibility that the environments module may have been reloaded. from .environments import PIPENV_PYTHON, PIPENV_YES @@ -475,7 +438,7 @@ def ensure_python(three=None, python=None): return path_to_python -def ensure_virtualenv(three=None, python=None, site_packages=None, pypi_mirror=None): +def ensure_virtualenv(project, three=None, python=None, site_packages=None, pypi_mirror=None): """Creates a virtualenv, if one doesn't exist.""" from .environments import PIPENV_USE_SYSTEM @@ -488,7 +451,7 @@ def ensure_virtualenv(three=None, python=None, site_packages=None, pypi_mirror=N # Ensure environment variables are set properly. ensure_environment() # Ensure Python is available. - python = ensure_python(three=three, python=python) + python = ensure_python(project, three=three, python=python) if python is not None and not isinstance(python, str): python = python.path.as_posix() # Create the virtualenv. @@ -506,13 +469,13 @@ def ensure_virtualenv(three=None, python=None, site_packages=None, pypi_mirror=N ) except KeyboardInterrupt: # If interrupted, cleanup the virtualenv. - cleanup_virtualenv(bare=False) + cleanup_virtualenv(project, bare=False) sys.exit(1) # If --three, --two, or --python were passed... elif (python) or (three is not None) or (site_packages is not None): USING_DEFAULT_PYTHON = False # Ensure python is installed before deleting existing virtual env - python = ensure_python(three=three, python=python) + python = ensure_python(project, three=three, python=python) if python is not None and not isinstance(python, str): python = python.path.as_posix() @@ -529,9 +492,10 @@ def ensure_virtualenv(three=None, python=None, site_packages=None, pypi_mirror=N crayons.normal(fix_utf8("Removing existing virtualenv..."), bold=True), err=True ) # Remove the virtualenv. - cleanup_virtualenv(bare=True) + cleanup_virtualenv(project, bare=True) # Call this function again. ensure_virtualenv( + project, three=three, python=python, site_packages=site_packages, @@ -540,6 +504,7 @@ def ensure_virtualenv(three=None, python=None, site_packages=None, pypi_mirror=N def ensure_project( + project, three=None, python=None, validate=True, @@ -567,6 +532,7 @@ def ensure_project( # Skip virtualenv creation when --system was used. if not system: ensure_virtualenv( + project, three=three, python=python, site_packages=site_packages, @@ -575,7 +541,7 @@ def ensure_project( if warn: # Warn users if they are using the wrong version of Python. if project.required_python_version: - path_to_python = which("python") or which("py") + path_to_python = project._which("python") or project._which("py") if path_to_python and project.required_python_version not in ( python_version(path_to_python) or "" ): @@ -605,7 +571,7 @@ def ensure_project( raise exceptions.DeployException # Ensure the Pipfile exists. ensure_pipfile( - validate=validate, skip_requirements=skip_requirements, system=system + project, validate=validate, skip_requirements=skip_requirements, system=system ) @@ -623,7 +589,7 @@ def shorten_path(location, bold=False): # return short -def do_where(virtualenv=False, bare=True): +def do_where(project, virtualenv=False, bare=True): """Executes the where functionality.""" if not virtualenv: if not project.pipfile_exists: @@ -655,7 +621,7 @@ def do_where(virtualenv=False, bare=True): click.echo(location) -def _cleanup_procs(procs, failed_deps_queue, retry=True): +def _cleanup_procs(project, procs, failed_deps_queue, retry=True): while not procs.empty(): c = procs.get() try: @@ -708,7 +674,7 @@ def _cleanup_procs(procs, failed_deps_queue, retry=True): failed_deps_queue.put(dep) -def batch_install(deps_list, procs, failed_deps_queue, +def batch_install(project, deps_list, procs, failed_deps_queue, requirements_dir, no_deps=True, ignore_hashes=False, allow_global=False, blocking=False, pypi_mirror=None, retry=True, sequential_deps=None): @@ -770,6 +736,7 @@ def batch_install(deps_list, procs, failed_deps_queue, is_sequential = sequential_deps and dep.name in sequential_dep_names is_blocking = any([dep.editable, dep.is_vcs, blocking, is_sequential]) c = pip_install( + project, dep, ignore_hashes=any([ignore_hashes, dep.editable, dep.is_vcs]), allow_global=allow_global, @@ -786,10 +753,11 @@ def batch_install(deps_list, procs, failed_deps_queue, procs.put(c) if procs.full() or procs.qsize() == len(deps_list) or is_sequential: - _cleanup_procs(procs, failed_deps_queue, retry=retry) + _cleanup_procs(project, procs, failed_deps_queue, retry=retry) def do_install_dependencies( + project, dev=False, dev_only=False, bare=False, @@ -835,7 +803,7 @@ def do_install_dependencies( deps_list = list(lockfile.get_requirements(dev=dev, only=dev_only)) if emit_requirements: index_args = prepare_pip_source_args( - get_source_list(pypi_mirror=pypi_mirror, project=project) + get_source_list(project, pypi_mirror=pypi_mirror, project=project) ) index_args = " ".join(index_args).replace(" -", "\n-") deps = [ @@ -861,11 +829,11 @@ def do_install_dependencies( } batch_install( - normal_deps, procs, failed_deps_queue, requirements_dir, **install_kwargs + project, normal_deps, procs, failed_deps_queue, requirements_dir, **install_kwargs ) if not procs.empty(): - _cleanup_procs(procs, failed_deps_queue) + _cleanup_procs(project, procs, failed_deps_queue) # click.echo(crayons.normal( # decode_for_output("Installing editable and vcs dependencies..."), bold=True @@ -890,10 +858,10 @@ def do_install_dependencies( retry_list.append(failed_dep) install_kwargs.update({"retry": False}) batch_install( - retry_list, procs, failed_deps_queue, requirements_dir, **install_kwargs + project, retry_list, procs, failed_deps_queue, requirements_dir, **install_kwargs ) if not procs.empty(): - _cleanup_procs(procs, failed_deps_queue, retry=False) + _cleanup_procs(project, procs, failed_deps_queue, retry=False) def convert_three_to_python(three, python): @@ -911,7 +879,7 @@ def convert_three_to_python(three, python): return python -def do_create_virtualenv(python=None, site_packages=None, pypi_mirror=None): +def do_create_virtualenv(project, python=None, site_packages=None, pypi_mirror=None): """Creates a virtualenv.""" click.echo( @@ -991,7 +959,7 @@ def do_create_virtualenv(python=None, site_packages=None, pypi_mirror=None): ) project._environment.add_dist("pipenv") # Say where the virtualenv is. - do_where(virtualenv=True, bare=False) + do_where(project, virtualenv=True, bare=False) def parse_download_fname(fname, name): @@ -1008,7 +976,7 @@ def parse_download_fname(fname, name): return version -def get_downloads_info(names_map, section): +def get_downloads_info(project, names_map, section): from .vendor.requirementslib.models.requirements import Requirement info = [] @@ -1020,7 +988,7 @@ def get_downloads_info(names_map, section): version = parse_download_fname(fname, name) # Get the hash of each file. cmd = [ - which_pip(), + which_pip(project), "hash", os.sep.join([project.download_location, fname]), ] @@ -1043,6 +1011,7 @@ def overwrite_dev(prod, dev): def do_lock( + project, ctx=None, system=False, clear=False, @@ -1095,7 +1064,7 @@ def do_lock( # Mutates the lockfile venv_resolve_deps( packages, - which=which, + which=project._which, project=project, dev=is_dev, clear=clear, @@ -1146,7 +1115,7 @@ def do_lock( return lockfile -def do_purge(bare=False, downloads=False, allow_global=False): +def do_purge(project, bare=False, downloads=False, allow_global=False): """Executes the purge functionality.""" if downloads: @@ -1176,7 +1145,7 @@ def do_purge(bare=False, downloads=False, allow_global=False): ) command = [ - which_pip(allow_global=allow_global), + which_pip(project, allow_global=allow_global), "uninstall", "-y", ] + list(to_remove) if environments.is_verbose(): @@ -1191,6 +1160,7 @@ def do_purge(bare=False, downloads=False, allow_global=False): def do_init( + project, dev=False, dev_only=False, emit_requirements=False, @@ -1219,13 +1189,13 @@ def do_init( if not system and not PIPENV_USE_SYSTEM: if not project.virtualenv_exists: try: - do_create_virtualenv(python=python, three=None, pypi_mirror=pypi_mirror) + do_create_virtualenv(project, python=python, three=None, pypi_mirror=pypi_mirror) except KeyboardInterrupt: - cleanup_virtualenv(bare=False) + cleanup_virtualenv(project, bare=False) sys.exit(1) # Ensure the Pipfile exists. if not deploy: - ensure_pipfile(system=system) + ensure_pipfile(project, system=system) if not requirements_dir: requirements_dir = vistir.path.create_tracked_tempdir( suffix="-requirements", prefix="pipenv-" @@ -1267,6 +1237,7 @@ def do_init( err=True, ) do_lock( + project, system=system, pre=pre, keep_outdated=keep_outdated, @@ -1290,6 +1261,7 @@ def do_init( err=True, ) do_lock( + project, system=system, pre=pre, keep_outdated=keep_outdated, @@ -1297,6 +1269,7 @@ def do_init( pypi_mirror=pypi_mirror, ) do_install_dependencies( + project, dev=dev, dev_only=dev_only, emit_requirements=emit_requirements, @@ -1319,6 +1292,7 @@ def do_init( def get_pip_args( + project, pre=False, # type: bool verbose=False, # type: bool upgrade=False, # type: bool @@ -1417,6 +1391,7 @@ def write_requirement_to_file( def pip_install( + project, requirement=None, r=None, allow_global=False, @@ -1469,7 +1444,7 @@ def pip_install( include_hashes=not ignore_hashes ) sources = get_source_list( - index, extra_indexes=extra_indexes, trusted_hosts=trusted_hosts, + project, index, extra_indexes=extra_indexes, trusted_hosts=trusted_hosts, pypi_mirror=pypi_mirror ) if r: @@ -1484,9 +1459,9 @@ def pip_install( err=True, ) - pip_command = [which("python", allow_global=allow_global), "-m", "pip", "install"] + pip_command = [project._which("python", allow_global=allow_global), "-m", "pip", "install"] pip_args = get_pip_args( - pre=pre, verbose=environments.is_verbose(), upgrade=True, + project, pre=pre, verbose=environments.is_verbose(), upgrade=True, selective_upgrade=selective_upgrade, no_use_pep517=not use_pep517, no_deps=no_deps, require_hashes=not ignore_hashes, ) @@ -1523,7 +1498,7 @@ def pip_install( return c -def pip_download(package_name): +def pip_download(project, package_name): cache_dir = vistir.compat.Path(PIPENV_CACHE_DIR) pip_config = { "PIP_CACHE_DIR": vistir.misc.fs_str(cache_dir.as_posix()), @@ -1534,7 +1509,7 @@ def pip_download(package_name): } for source in project.sources: cmd = [ - which_pip(), + which_pip(project), "download", package_name, "-i", source["url"], @@ -1581,7 +1556,7 @@ def fallback_which(command, location=None, allow_global=False, system=False): return "" -def which_pip(allow_global=False): +def which_pip(project, allow_global=False): """Returns the location of virtualenv-installed pip.""" location = None @@ -1589,7 +1564,7 @@ def which_pip(allow_global=False): location = os.environ["VIRTUAL_ENV"] if allow_global: if location: - pip = which("pip", location=location) + pip = project._which("pip", location=location) if pip: return pip @@ -1598,7 +1573,7 @@ def which_pip(allow_global=False): if where: return where - pip = which("pip") + pip = project._which("pip") if not pip: pip = fallback_which("pip", allow_global=allow_global, location=location) return pip @@ -1750,7 +1725,7 @@ def warn_in_virtualenv(): ) -def ensure_lockfile(keep_outdated=False, pypi_mirror=None): +def ensure_lockfile(project, keep_outdated=False, pypi_mirror=None): """Ensures that the lockfile is up-to-date.""" if not keep_outdated: keep_outdated = project.settings.get("keep_outdated") @@ -1768,12 +1743,12 @@ def ensure_lockfile(keep_outdated=False, pypi_mirror=None): ), err=True, ) - do_lock(keep_outdated=keep_outdated, pypi_mirror=pypi_mirror) + do_lock(project, keep_outdated=keep_outdated, pypi_mirror=pypi_mirror) else: - do_lock(keep_outdated=keep_outdated, pypi_mirror=pypi_mirror) + do_lock(project, keep_outdated=keep_outdated, pypi_mirror=pypi_mirror) -def do_py(system=False): +def do_py(project, system=False): if not project.virtualenv_exists: click.echo( "{}({}){}".format( @@ -1786,12 +1761,12 @@ def do_py(system=False): return try: - click.echo(which("python", allow_global=system)) + click.echo(project._which("python", allow_global=system)) except AttributeError: click.echo(crayons.red("No project found!")) -def do_outdated(pypi_mirror=None, pre=False, clear=False): +def do_outdated(project, pypi_mirror=None, pre=False, clear=False): # TODO: Allow --skip-lock here? from collections import namedtuple @@ -1817,7 +1792,7 @@ def do_outdated(pypi_mirror=None, pre=False, clear=False): dep = Requirement.from_line(str(result.as_requirement())) packages.update(dep.as_pipfile()) updated_packages = {} - lockfile = do_lock(clear=clear, pre=pre, write=False, pypi_mirror=pypi_mirror) + lockfile = do_lock(project, clear=clear, pre=pre, write=False, pypi_mirror=pypi_mirror) for section in ("develop", "default"): for package in lockfile[section]: try: @@ -1870,6 +1845,7 @@ def do_outdated(pypi_mirror=None, pre=False, clear=False): def do_install( + project, packages=False, editable_packages=False, index_url=False, @@ -1910,6 +1886,7 @@ def do_install( concurrent = not sequential # Ensure that virtualenv is available and pipfile are available ensure_project( + project, three=three, python=python, system=system, @@ -1981,7 +1958,7 @@ def do_install( err=True, ) try: - import_requirements(r=project.path_to(requirementstxt), dev=dev) + import_requirements(project, r=project.path_to(requirementstxt), dev=dev) except (UnicodeDecodeError, PipError) as e: # Don't print the temp file path if remote since it will be deleted. req_path = requirements_url if remote else project.path_to(requirementstxt) @@ -2042,6 +2019,7 @@ def do_install( if pre: project.update_settings({"allow_prereleases": pre}) do_init( + project, dev=dev, allow_global=system, ignore_pipfile=ignore_pipfile, @@ -2063,6 +2041,7 @@ def do_install( pkg_list = packages + [f'-e {pkg}' for pkg in editable_packages] if not system and not project.virtualenv_exists: do_init( + project, dev=dev, system=system, allow_global=system, @@ -2103,6 +2082,7 @@ def do_install( if environments.is_verbose(): sp.hide_and_write(f"Installing package: {pkg_requirement.as_line(include_hashes=False)}") c = pip_install( + project, pkg_requirement, ignore_hashes=True, allow_global=system, @@ -2183,6 +2163,7 @@ def do_install( if pip_shims_module: os.environ["PIP_SHIMS_BASE_MODULE"] = pip_shims_module do_init( + project, dev=dev, system=system, allow_global=system, @@ -2197,6 +2178,7 @@ def do_install( def do_uninstall( + project, packages=False, editable_packages=False, three=None, @@ -2219,7 +2201,7 @@ def do_uninstall( # Ensure that virtualenv is available. # TODO: We probably shouldn't ensure a project exists if the outcome will be to just # install things in order to remove them... maybe tell the user to install first? - ensure_project(three=three, python=python, pypi_mirror=pypi_mirror) + ensure_project(project, three=three, python=python, pypi_mirror=pypi_mirror) # Un-install all dependencies, if --all was provided. if not any([packages, editable_packages, all_dev, all]): raise exceptions.PipenvUsageError("No package provided!", ctx=ctx) @@ -2275,7 +2257,7 @@ def do_uninstall( )), bold=True ) ) - do_purge(bare=False, allow_global=system) + do_purge(project, bare=False, allow_global=system) sys.exit(0) selected_pkg_map = { @@ -2296,7 +2278,7 @@ def do_uninstall( if package_name in packages_to_remove: with project.environment.activated(): if pip_path is None: - pip_path = which_pip(allow_global=system) + pip_path = which_pip(project, allow_global=system) cmd = [pip_path, "uninstall", package_name, "-y"] c = run_command(cmd) click.echo(crayons.cyan(c.stdout)) @@ -2339,14 +2321,14 @@ def do_uninstall( if in_packages: project.remove_package_from_pipfile(package_name, dev=False) if lock: - do_lock(system=system, keep_outdated=keep_outdated, pypi_mirror=pypi_mirror) + do_lock(project, system=system, keep_outdated=keep_outdated, pypi_mirror=pypi_mirror) sys.exit(int(failure)) -def do_shell(three=None, python=False, fancy=False, shell_args=None, pypi_mirror=None): +def do_shell(project, three=None, python=False, fancy=False, shell_args=None, pypi_mirror=None): # Ensure that virtualenv is available. ensure_project( - three=three, python=python, validate=False, pypi_mirror=pypi_mirror, + project, three=three, python=python, validate=False, pypi_mirror=pypi_mirror, ) # Support shell compatibility mode. @@ -2386,9 +2368,9 @@ def do_shell(three=None, python=False, fancy=False, shell_args=None, pypi_mirror shell.fork(*fork_args) -def _inline_activate_virtualenv(): +def _inline_activate_virtualenv(project): try: - activate_this = which("activate_this.py") + activate_this = project._which("activate_this.py") if not activate_this or not os.path.exists(activate_this): raise exceptions.VirtualenvActivationException() with open(activate_this) as f: @@ -2405,7 +2387,7 @@ def _inline_activate_virtualenv(): ) -def _inline_activate_venv(): +def _inline_activate_venv(project): """Built-in venv doesn't have activate_this.py, but doesn't need it anyway. As long as we find the correct executable, built-in venv sets up the @@ -2423,12 +2405,12 @@ def _inline_activate_venv(): os.environ["PATH"] = os.pathsep.join(components) -def inline_activate_virtual_environment(): +def inline_activate_virtual_environment(project): root = project.virtualenv_location if os.path.exists(os.path.join(root, "pyvenv.cfg")): - _inline_activate_venv() + _inline_activate_venv(project) else: - _inline_activate_virtualenv() + _inline_activate_virtualenv(project) if "VIRTUAL_ENV" not in os.environ: os.environ["VIRTUAL_ENV"] = vistir.misc.fs_str(root) @@ -2456,13 +2438,13 @@ def _launch_windows_subprocess(script): return subprocess.Popen(script.cmdify(), shell=True, **options) -def do_run_nt(script): +def do_run_nt(project, script): p = _launch_windows_subprocess(script) p.communicate() sys.exit(p.returncode) -def do_run_posix(script, command): +def do_run_posix(project, script, command): command_path = system_which(script.command) if not command_path: if project.has_script(command): @@ -2493,7 +2475,7 @@ def do_run_posix(script, command): ) -def do_run(command, args, three=None, python=False, pypi_mirror=None): +def do_run(project, command, args, three=None, python=False, pypi_mirror=None): """Attempt to run command either pulling from project or interpreting as executable. Args are appended to the command in [scripts] section of project if found. @@ -2502,15 +2484,15 @@ def do_run(command, args, three=None, python=False, pypi_mirror=None): # Ensure that virtualenv is available. ensure_project( - three=three, python=python, validate=False, pypi_mirror=pypi_mirror, + project, three=three, python=python, validate=False, pypi_mirror=pypi_mirror, ) - load_dot_env() + load_dot_env(project) previous_pip_shims_module = os.environ.pop("PIP_SHIMS_BASE_MODULE", None) # Activate virtualenv under the current interpreter's environment - inline_activate_virtual_environment() + inline_activate_virtual_environment(project) # Set an environment variable, so we know we're in the environment. # Only set PIPENV_ACTIVE after finishing reading virtualenv_location @@ -2528,7 +2510,7 @@ def do_run(command, args, three=None, python=False, pypi_mirror=None): click.echo(crayons.normal(f"$ {cmd_string}"), err=True) except ScriptEmptyError: click.echo("Can't run script {0!r}-it's empty?", err=True) - run_args = [script] + run_args = [project, script] run_kwargs = {} if os.name == "nt": run_fn = do_run_nt @@ -2546,6 +2528,7 @@ def do_run(command, args, three=None, python=False, pypi_mirror=None): def do_check( + project, three=None, python=False, system=False, @@ -2564,6 +2547,7 @@ def do_check( if not system: # Ensure that virtualenv is available. ensure_project( + project, three=three, python=python, validate=False, @@ -2599,7 +2583,7 @@ def do_check( os.path.dirname(os.path.abspath(__file__)), "patched", "safety" ) if not system: - python = which("python") + python = project._which("python") else: python = first(system_which(p) for p in ("python", "python3", "python2")) if not python: @@ -2705,12 +2689,12 @@ def do_check( sys.exit(c.returncode) -def do_graph(bare=False, json=False, json_tree=False, reverse=False): +def do_graph(project, bare=False, json=False, json_tree=False, reverse=False): from pipenv.vendor import pipdeptree from pipenv.vendor.vistir.compat import JSONDecodeError pipdeptree_path = pipdeptree.__file__.rstrip("cdo") try: - python_path = which("python") + python_path = project._which("python") except AttributeError: click.echo( "{}: {}".format( @@ -2844,7 +2828,7 @@ def do_graph(bare=False, json=False, json_tree=False, reverse=False): def do_sync( - ctx, + project, dev=False, three=None, python=None, @@ -2864,6 +2848,7 @@ def do_sync( # Ensure that virtualenv is available if not system. ensure_project( + project, three=three, python=python, validate=False, @@ -2876,6 +2861,7 @@ def do_sync( suffix="-requirements", prefix="pipenv-" ) do_init( + project, dev=dev, allow_global=system, concurrent=(not sequential), @@ -2890,13 +2876,13 @@ def do_sync( def do_clean( - ctx, three=None, python=None, dry_run=False, bare=False, pypi_mirror=None, + project, three=None, python=None, dry_run=False, bare=False, pypi_mirror=None, system=False ): # Ensure that virtualenv is available. from packaging.utils import canonicalize_name - ensure_project(three=three, python=python, validate=False, pypi_mirror=pypi_mirror) - ensure_lockfile(pypi_mirror=pypi_mirror) + ensure_project(project, three=three, python=python, validate=False, pypi_mirror=pypi_mirror) + ensure_lockfile(project, pypi_mirror=pypi_mirror) # Make sure that the virtualenv's site packages are configured correctly # otherwise we may end up removing from the global site packages directory installed_package_names = project.installed_package_names.copy() @@ -2914,7 +2900,7 @@ def do_clean( if used_package in installed_package_names: installed_package_names.remove(used_package) failure = False - cmd = [which_pip(allow_global=system), "uninstall", "-y", "-qq"] + cmd = [which_pip(project, allow_global=system), "uninstall", "-y", "-qq"] for apparent_bad_package in installed_package_names: if dry_run and not bare: click.echo(apparent_bad_package) @@ -2926,7 +2912,7 @@ def do_clean( ) ) # Uninstall the package. - cmd = [which_pip(), "uninstall", apparent_bad_package, "-y"] + cmd = [which_pip(project), "uninstall", apparent_bad_package, "-y"] c = run_command(cmd) if c.returncode != 0: failure = True diff --git a/pipenv/exceptions.py b/pipenv/exceptions.py index 08f56cbf..e14ac1e6 100644 --- a/pipenv/exceptions.py +++ b/pipenv/exceptions.py @@ -258,17 +258,6 @@ class SystemUsageError(PipenvOptionsError): super().__init__(option_name, message=message, ctx=ctx, extra=extra, **kwargs) -class PipfileException(PipenvFileError): - def __init__(self, hint=None, **kwargs): - from .core import project - - if not hint: - hint = "{} {}".format(crayons.red("ERROR (PACKAGE NOT INSTALLED):"), hint) - filename = project.pipfile_location - extra = kwargs.pop("extra", []) - PipenvFileError.__init__(self, filename, hint, extra=extra, **kwargs) - - class SetupException(PipenvException): def __init__(self, message=None, **kwargs): PipenvException.__init__(self, message, **kwargs) diff --git a/pipenv/help.py b/pipenv/help.py index 6d2f62d9..2882bca1 100644 --- a/pipenv/help.py +++ b/pipenv/help.py @@ -4,7 +4,6 @@ import sys import pipenv -from pipenv.core import project from pipenv.pep508checker import lookup from pipenv.vendor import pythonfinder @@ -16,7 +15,7 @@ def print_utf(line): print(line.encode("utf-8")) -def get_pipenv_diagnostics(): +def get_pipenv_diagnostics(project): print("
$ pipenv --support") print("") print(f"Pipenv version: `{pipenv.__version__!r}`") @@ -82,4 +81,5 @@ def get_pipenv_diagnostics(): if __name__ == "__main__": - get_pipenv_diagnostics() + from pipenv.project import Project + get_pipenv_diagnostics(Project()) diff --git a/pipenv/project.py b/pipenv/project.py index 47f72ea0..2a6c47dc 100644 --- a/pipenv/project.py +++ b/pipenv/project.py @@ -11,34 +11,37 @@ import re import sys import urllib.parse +import pipfile +import pipfile.api import toml import tomlkit import vistir -import pipfile -import pipfile.api - -from pipenv.vendor.cached_property import cached_property - from pipenv.cmdparse import Script +from pipenv.core import system_which from pipenv.environment import Environment from pipenv.environments import ( PIPENV_DEFAULT_PYTHON_VERSION, PIPENV_IGNORE_VIRTUALENVS, PIPENV_MAX_DEPTH, - PIPENV_PIPFILE, PIPENV_PYTHON, PIPENV_TEST_INDEX, PIPENV_VENV_IN_PROJECT, - PIPENV_USE_SYSTEM, is_in_virtualenv, is_type_checking, is_using_venv + PIPENV_PIPFILE, PIPENV_PYTHON, PIPENV_TEST_INDEX, PIPENV_USE_SYSTEM, + PIPENV_VENV_IN_PROJECT, is_in_virtualenv, is_type_checking, is_using_venv ) -from pipenv.vendor.requirementslib.models.utils import get_default_pyproject_backend from pipenv.utils import ( cleanup_toml, convert_toml_outline_tables, find_requirements, - get_canonical_names, get_url_name, get_workon_home, is_editable, - is_installable_file, is_star, is_valid_url, is_virtual_environment, - looks_like_dir, normalize_drive, pep423_name, proper_case, python_version, - safe_expandvars, get_pipenv_dist + find_windows_executable, get_canonical_names, get_pipenv_dist, get_url_name, + get_workon_home, is_editable, is_installable_file, is_star, is_valid_url, + is_virtual_environment, looks_like_dir, normalize_drive, pep423_name, + proper_case, python_version, safe_expandvars +) +from pipenv.vendor.cached_property import cached_property +from pipenv.vendor.requirementslib.models.utils import ( + get_default_pyproject_backend ) + if is_type_checking(): - import pkg_resources from typing import Dict, List, Optional, Set, Text, Tuple, Union + + import pkg_resources TSource = Dict[Text, Union[Text, bool]] TPackageEntry = Dict[str, Union[bool, str, List[str]]] TPackage = Dict[str, TPackageEntry] @@ -134,13 +137,12 @@ class SourceNotFound(KeyError): pass -class Project(object): +class Project: """docstring for Project""" _lockfile_encoder = _LockFileEncoder() - def __init__(self, which=None, python_version=None, chdir=True): - super(Project, self).__init__() + def __init__(self, python_version=None, chdir=True): self._name = None self._virtualenv_location = None self._download_location = None @@ -151,7 +153,6 @@ class Project(object): self._requirements_location = None self._original_dir = os.path.abspath(os.curdir) self._environment = None - self._which = which self._build_system = { "requires": ["setuptools", "wheel"] } @@ -652,7 +653,8 @@ class Project(object): @property def _pipfile(self): - from .vendor.requirementslib.models.pipfile import Pipfile as ReqLibPipfile + from .vendor.requirementslib.models.pipfile import \ + Pipfile as ReqLibPipfile pf = ReqLibPipfile.load(self.pipfile_location) return pf @@ -735,6 +737,7 @@ class Project(object): def create_pipfile(self, python=None): """Creates the Pipfile, filled with juicy defaults.""" from .vendor.pip_shims.shims import InstallCommand + # Inherit the pip's index configuration of install command. command = InstallCommand() indexes = command.cmd_opts.get_option("--extra-index-url").default @@ -780,7 +783,8 @@ class Project(object): return source def get_or_create_lockfile(self, from_pipfile=False): - from pipenv.vendor.requirementslib.models.lockfile import Lockfile as Req_Lockfile + from pipenv.vendor.requirementslib.models.lockfile import \ + Lockfile as Req_Lockfile lockfile = None if from_pipfile and self.pipfile_exists: lockfile_dict = { @@ -1141,3 +1145,29 @@ class Project(object): if as_path: result = str(result.path) return result + + def _which(self, command, location=None, allow_global=False): + if not allow_global and location is None: + if self.virtualenv_exists: + location = self.virtualenv_location + else: + location = os.environ.get("VIRTUAL_ENV", None) + if not (location and os.path.exists(location)) and not allow_global: + raise RuntimeError("location not created nor specified") + + version_str = "python{}".format(".".join([str(v) for v in sys.version_info[:2]])) + is_python = command in ("python", os.path.basename(sys.executable), version_str) + if not allow_global: + if os.name == "nt": + p = find_windows_executable(os.path.join(location, "Scripts"), command) + else: + p = os.path.join(location, "bin", command) + else: + if is_python: + p = sys.executable + if not os.path.exists(p): + if is_python: + p = sys.executable or system_which("python") + else: + p = system_which(command) + return p diff --git a/pipenv/resolver.py b/pipenv/resolver.py index a3e75c13..0e82f942 100644 --- a/pipenv/resolver.py +++ b/pipenv/resolver.py @@ -688,7 +688,8 @@ def resolve_packages(pre, clear, verbose, system, write, requirements_dir, packa req_dir=requirements_dir ) - from pipenv.core import project + from pipenv.project import Project + project = Project() sources = ( replace_pypi_sources(project.pipfile_sources, pypi_mirror_source) if pypi_mirror_source diff --git a/pipenv/utils.py b/pipenv/utils.py index e5d947c5..0dad2ae9 100644 --- a/pipenv/utils.py +++ b/pipenv/utils.py @@ -273,11 +273,9 @@ def prepare_pip_source_args(sources, pip_args=None): return pip_args -def get_project_index(index=None, trusted_hosts=None, project=None): +def get_project_index(project, index=None, trusted_hosts=None): # type: (Optional[Union[str, TSource]], Optional[List[str]], Optional[Project]) -> TSource from .project import SourceNotFound - if not project: - from .core import project if trusted_hosts is None: trusted_hosts = [] if isinstance(index, Mapping): @@ -293,23 +291,21 @@ def get_project_index(index=None, trusted_hosts=None, project=None): def get_source_list( + project, # type: Project index=None, # type: Optional[Union[str, TSource]] extra_indexes=None, # type: Optional[List[str]] trusted_hosts=None, # type: Optional[List[str]] pypi_mirror=None, # type: Optional[str] - project=None, # type: Optional[Project] ): # type: (...) -> List[TSource] sources = [] # type: List[TSource] - if not project: - from .core import project if index: - sources.append(get_project_index(index)) + sources.append(get_project_index(project, index)) if extra_indexes: if isinstance(extra_indexes, str): extra_indexes = [extra_indexes] for source in extra_indexes: - extra_src = get_project_index(source) + extra_src = get_project_index(project, source) if not sources or extra_src["url"] != sources[0]["url"]: sources.append(extra_src) else: @@ -326,10 +322,8 @@ def get_source_list( return sources -def get_indexes_from_requirement(req, project=None, index=None, extra_indexes=None, trusted_hosts=None, pypi_mirror=None): - # type: (Requirement, Optional[Project], Optional[Text], Optional[List[Text]], Optional[List[Text]], Optional[Text]) -> Tuple[TSource, List[TSource], List[Text]] - if not project: - from .core import project +def get_indexes_from_requirement(req, project, ndex=None, extra_indexes=None, trusted_hosts=None, pypi_mirror=None): + # type: (Requirement, Project, Optional[Text], Optional[List[Text]], Optional[List[Text]], Optional[Text]) -> Tuple[TSource, List[TSource], List[Text]] index_sources = [] # type: List[TSource] if not trusted_hosts: trusted_hosts = [] # type: List[Text] @@ -630,9 +624,9 @@ class Resolver: def create( cls, deps, # type: List[str] + project, # type: Project index_lookup=None, # type: Dict[str, str] markers_lookup=None, # type: Dict[str, str] - project=None, # type: Project sources=None, # type: List[str] req_dir=None, # type: str clear=False, # type: bool @@ -646,9 +640,6 @@ class Resolver: index_lookup = {} if markers_lookup is None: markers_lookup = {} - if project is None: - from pipenv.core import project - project = project if sources is None: sources = project.sources constraints, skipped, index_lookup, markers_lookup = cls.get_metadata( @@ -661,11 +652,9 @@ class Resolver: ) @classmethod - def from_pipfile(cls, project=None, pipfile=None, dev=False, pre=False, clear=False): + def from_pipfile(cls, project, pipfile=None, dev=False, pre=False, clear=False): # type: (Optional[Project], Optional[Pipfile], bool, bool, bool) -> "Resolver" from pipenv.vendor.vistir.path import create_tracked_tempdir - if not project: - from pipenv.core import project if not pipfile: pipfile = project._pipfile req_dir = create_tracked_tempdir(suffix="-requirements", prefix="pipenv-") @@ -1097,7 +1086,7 @@ def actually_resolve_deps( with warnings.catch_warnings(record=True) as warning_list: resolver = Resolver.create( - deps, index_lookup, markers_lookup, project, sources, req_dir, clear, pre + deps, project, index_lookup, markers_lookup, sources, req_dir, clear, pre ) resolver.resolve() hashes = resolver.resolve_hashes() diff --git a/tests/conftest.py b/tests/conftest.py index e69de29b..fc75b6b5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -0,0 +1,7 @@ +import pytest + + +@pytest.fixture() +def project(): + from pipenv.project import Project + return Project() diff --git a/tests/integration/test_install_markers.py b/tests/integration/test_install_markers.py index f212bc1b..96afc37b 100644 --- a/tests/integration/test_install_markers.py +++ b/tests/integration/test_install_markers.py @@ -1,5 +1,4 @@ import os -import sys import pytest diff --git a/tests/unit/test_core.py b/tests/unit/test_core.py index d7981a47..3c4c1e23 100644 --- a/tests/unit/test_core.py +++ b/tests/unit/test_core.py @@ -20,7 +20,7 @@ def test_suppress_nested_venv_warning(capsys): @pytest.mark.core -def test_load_dot_env_from_environment_variable_location(monkeypatch, capsys): +def test_load_dot_env_from_environment_variable_location(monkeypatch, capsys, project): with temp_environ(), monkeypatch.context() as m, TemporaryDirectory(prefix='pipenv-', suffix='') as tempdir: if os.name == "nt": import click @@ -33,12 +33,12 @@ def test_load_dot_env_from_environment_variable_location(monkeypatch, capsys): m.setenv("PIPENV_DOTENV_LOCATION", str(dotenv_path)) m.setattr("pipenv.environments.PIPENV_DOTENV_LOCATION", str(dotenv_path)) - load_dot_env() + load_dot_env(project) assert os.environ[key] == val @pytest.mark.core -def test_doesnt_load_dot_env_if_disabled(monkeypatch, capsys): +def test_doesnt_load_dot_env_if_disabled(monkeypatch, capsys, project): with temp_environ(), monkeypatch.context() as m, TemporaryDirectory(prefix='pipenv-', suffix='') as tempdir: if os.name == "nt": import click @@ -52,15 +52,15 @@ def test_doesnt_load_dot_env_if_disabled(monkeypatch, capsys): m.setenv("PIPENV_DOTENV_LOCATION", str(dotenv_path)) m.setattr("pipenv.environments.PIPENV_DOTENV_LOCATION", str(dotenv_path)) m.setattr("pipenv.environments.PIPENV_DONT_LOAD_ENV", True) - load_dot_env() + load_dot_env(project) assert key not in os.environ m.setattr("pipenv.environments.PIPENV_DONT_LOAD_ENV", False) - load_dot_env() + load_dot_env(project) assert key in os.environ @pytest.mark.core -def test_load_dot_env_warns_if_file_doesnt_exist(monkeypatch, capsys): +def test_load_dot_env_warns_if_file_doesnt_exist(monkeypatch, capsys, project): with temp_environ(), monkeypatch.context() as m, TemporaryDirectory(prefix='pipenv-', suffix='') as tempdir: if os.name == "nt": import click @@ -69,6 +69,6 @@ def test_load_dot_env_warns_if_file_doesnt_exist(monkeypatch, capsys): dotenv_path = os.path.join(tempdir.name, 'does-not-exist.env') m.setenv("PIPENV_DOTENV_LOCATION", str(dotenv_path)) m.setattr("pipenv.environments.PIPENV_DOTENV_LOCATION", str(dotenv_path)) - load_dot_env() + load_dot_env(project) output, err = capsys.readouterr() assert 'Warning' in err