diff --git a/news/3386.trivial.rst b/news/3386.trivial.rst new file mode 100644 index 0000000000..632b815303 --- /dev/null +++ b/news/3386.trivial.rst @@ -0,0 +1 @@ +Decouple project from the existence of Pipfile. diff --git a/pipenv/cli/command.py b/pipenv/cli/command.py index ec1bef6138..84dd0c21ca 100644 --- a/pipenv/cli/command.py +++ b/pipenv/cli/command.py @@ -154,7 +154,11 @@ def cli( # There is no virtualenv yet. if not project.virtualenv_exists: echo( - crayons.red("No virtualenv has been created for this project yet!"), + "{}({}){}".format( + crayons.red("No virtualenv has been created for this project"), + crayons.white(project.project_directory, bold=True), + crayons.red(" yet!") + ), err=True, ) ctx.abort() diff --git a/pipenv/core.py b/pipenv/core.py index 4fb7939e0f..c6d76f1c09 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -540,11 +540,16 @@ def ensure_project( # Automatically use an activated virtualenv. if PIPENV_USE_SYSTEM: system = True - if not project.pipfile_exists: - if deploy is True: - raise exceptions.PipfileNotFound - else: - project.touch_pipfile() + if not project.pipfile_exists and deploy: + raise exceptions.PipfileNotFound + # Fail if working under / + if not project.name: + click.echo( + "{0}: Pipenv is not intended to work under the root directory, " + "please choose another path.".format(crayons.red("ERROR")), + err=True + ) + sys.exit(1) # Skip virtualenv creation when --system was used. if not system: ensure_virtualenv( @@ -607,24 +612,24 @@ def shorten_path(location, bold=False): def do_where(virtualenv=False, bare=True): """Executes the where functionality.""" if not virtualenv: - location = project.pipfile_location - # Shorten the virtual display of the path to the virtualenv. - if not bare: - location = shorten_path(location) - if not location: + if not project.pipfile_exists: click.echo( "No Pipfile present at project home. Consider running " "{0} first to automatically generate a Pipfile for you." "".format(crayons.green("`pipenv install`")), err=True, ) + return + location = project.pipfile_location + # Shorten the virtual display of the path to the virtualenv. + if not bare: + location = shorten_path(location) elif not bare: click.echo( "Pipfile found at {0}.\n Considering this to be the project home." "".format(crayons.green(location)), err=True, ) - pass else: click.echo(project.project_directory) else: diff --git a/pipenv/project.py b/pipenv/project.py index e8550a041a..aeb7a40f9f 100644 --- a/pipenv/project.py +++ b/pipenv/project.py @@ -222,7 +222,7 @@ def name(self): @property def pipfile_exists(self): - return bool(self.pipfile_location) + return os.path.isfile(self.pipfile_location) @property def required_python_version(self): @@ -237,11 +237,7 @@ def required_python_version(self): @property def project_directory(self): - if self.pipfile_location is not None: - return os.path.abspath(os.path.join(self.pipfile_location, os.pardir)) - - else: - return None + return os.path.abspath(os.path.join(self.pipfile_location, os.pardir)) @property def requirements_exists(self): @@ -255,8 +251,7 @@ def is_venv_in_project(self): @property def virtualenv_exists(self): - # TODO: Decouple project from existence of Pipfile. - if self.pipfile_exists and os.path.exists(self.virtualenv_location): + if os.path.exists(self.virtualenv_location): if os.name == "nt": extra = ["Scripts", "activate.bat"] else: @@ -470,7 +465,7 @@ def pipfile_location(self): try: loc = pipfile.Pipfile.find(max_depth=PIPENV_MAX_DEPTH) except RuntimeError: - loc = None + loc = "Pipfile" self._pipfile_location = _normalized(loc) return self._pipfile_location @@ -499,6 +494,8 @@ def parsed_pipfile(self): def read_pipfile(self): # Open the pipfile, read it into memory. + if not self.pipfile_exists: + return "" with io.open(self.pipfile_location) as f: contents = f.read() self._pipfile_newlines = preferred_newlines(f) @@ -659,11 +656,6 @@ def dev_packages(self): """Returns a list of dev-packages, for pip-tools to consume.""" return self._build_package_list("dev-packages") - def touch_pipfile(self): - """Simply touches the Pipfile, for later use.""" - with open("Pipfile", "a"): - os.utime("Pipfile", None) - @property def pipfile_is_empty(self): if not self.pipfile_exists: @@ -680,7 +672,6 @@ def create_pipfile(self, python=None): ConfigOptionParser, make_option_group, index_group ) - name = self.name if self.name is not None else "Pipfile" config_parser = ConfigOptionParser(name=self.name) config_parser.add_option_group(make_option_group(index_group, config_parser)) install = config_parser.option_groups[0] @@ -834,7 +825,7 @@ def write_lockfile(self, content): @property def pipfile_sources(self): - if "source" not in self.parsed_pipfile: + if self.pipfile_is_empty or "source" not in self.parsed_pipfile: return [DEFAULT_SOURCE] # We need to make copies of the source info so we don't # accidentally modify the cache. See #2100 where values are