Repository: python-poetry/poetry

Issue:
Making a virtualenv in the project root fails if <cache-dir>/virtualenvs does not exist
<!--
  Hi there! Thank you for discovering and submitting an issue.

  Before you submit this; let's make sure of a few things.
  Please make sure the following boxes are ticked if they are correct.
  If not, please try and fulfill these first.
-->

<!-- Checked checkbox should look like this: [x] -->
- [x] I am on the [latest](https://github.com/sdispater/poetry/releases/latest) Poetry version.
- [x] I have searched the [issues](https://github.com/sdispater/poetry/issues) of this repo and believe that this is not a duplicate.
- [x] If an exception occurs when executing a command, I executed it again in debug mode (`-vvv` option).

<!--
  Once those are done, if you're able to fill in the following list with your information,
  it'd be very helpful to whoever handles the issue.
-->

- **OS version and name**: macOS Catalina (10.15.1)
- **Poetry version**: 1.0.0b3
- **Link of a [Gist](https://gist.github.com/) with the contents of your pyproject.toml file**: N/A

## Issue
<!-- Now feel free to write your issue, but please be descriptive! Thanks again 🙌 ❤️ -->
It appears that if `poetry` has never made a virtualenv for you—which means that `<cache-dir>/virtualenvs` does not exist, then asking `poetry` to make a virtualenv in the project root will fail.

```shell
$ poetry env use python3 -vvv                                                                           11:12:03
Creating virtualenv autoencoding in /Users/will.frey/Desktop/autoencoding/.venv

[IOError]
[Errno 2] No such file or directory: '/Users/will.frey/Library/Caches/pypoetry/virtualenvs/envs.toml'

Traceback (most recent call last):
Traceback (most recent call last):
  File "/usr/local/bin/poetry", line 14, in <module>
    main()
  File "/Users/will.frey/.poetry/lib/poetry/console/__init__.py", line 5, in main
    return Application().run()
  File "/Users/will.frey/.poetry/lib/poetry/_vendor/py2.7/clikit/console_application.py", line 139, in run
    trace.render(io, simple=isinstance(e, CliKitException))
  File "/Users/will.frey/.poetry/lib/poetry/_vendor/py2.7/clikit/ui/components/exception_trace.py", line 57, in render
    self._render_traceback(io, tb)
  File "/Users/will.frey/.poetry/lib/poetry/_vendor/py2.7/clikit/ui/components/exception_trace.py", line 67, in _render_traceback
    io.write_line("".join(traceback.format_list(frames)))
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/traceback.py", line 39, in format_list
    item = '  File "%s", line %d, in %s\n' % (filename,lineno,name)
TypeError: %d format: a number is required, not str
```

I can get past this by either having first made a not-in-project-root virtualenv with `poetry` or by creating the `<cache-dir>/virtualenvs` directory manually myself.



Discussion:
I have similar problem when cloning project with `in-project = true` in poetry.toml` and freshly setup poetry (no single virtualenv created yet). Got this error:-

```
git clone <url>
cd x4antispam
poetry env use python3
/home/kamal/.poetry/lib/poetry/_vendor/py2.7/subprocess32.py:149: RuntimeWarning: The _posixsubprocess module is not being used. Child proce
ss reliability may suffer if your program uses threads.
  "program uses threads.", RuntimeWarning)
No handlers could be found for logger "keyring.backend"
Recreating virtualenv x4antispam in /home/kamal/git/x4antispam/.venv

[IOError]
[Errno 2] No such file or directory: '/home/kamal/.cache/pypoetry/virtualenvs/envs.toml'
```

```
Poetry version 1.0.0b5
```
Maybe we can skip writing the envs file in lib/poetry/utils/env.py:-

```
venv_in_project = self._poetry.config.get("virtualenvs.in-project")
if not venv_in_project:
    envs_file.write(envs)
```
Ok, so one workaround is to create the "global" virtualenvs dir first:-

```
mkdir -p $HOME/.cache/pypoetry/virtualenvs/
```

Resolving patch:
diff --git a/poetry/utils/env.py b/poetry/utils/env.py
--- a/poetry/utils/env.py
+++ b/poetry/utils/env.py
@@ -208,6 +208,24 @@ def activate(self, python, io):  # type: (str, IO) -> Env
         patch = python_version.text
 
         create = False
+        is_root_venv = self._poetry.config.get("virtualenvs.in-project")
+        # If we are required to create the virtual environment in the root folder,
+        # create or recreate it if needed
+        if is_root_venv:
+            create = False
+            venv = self._poetry.file.parent / ".venv"
+            if venv.exists():
+                # We need to check if the patch version is correct
+                _venv = VirtualEnv(venv)
+                current_patch = ".".join(str(v) for v in _venv.version_info[:3])
+
+                if patch != current_patch:
+                    create = True
+
+            self.create_venv(io, executable=python, force=create)
+
+            return self.get(reload=True)
+
         envs = tomlkit.document()
         base_env_name = self.generate_env_name(self._poetry.package.name, str(cwd))
         if envs_file.exists():


Test patch:
diff --git a/tests/utils/test_env.py b/tests/utils/test_env.py
--- a/tests/utils/test_env.py
+++ b/tests/utils/test_env.py
@@ -661,3 +661,38 @@ def test_create_venv_does_not_try_to_find_compatible_versions_with_executable(
 
     assert expected_message == str(e.value)
     assert 0 == m.call_count
+
+
+def test_activate_with_in_project_setting_does_not_fail_if_no_venvs_dir(
+    manager, poetry, config, tmp_dir, mocker
+):
+    if "VIRTUAL_ENV" in os.environ:
+        del os.environ["VIRTUAL_ENV"]
+
+    config.merge(
+        {
+            "virtualenvs": {
+                "path": str(Path(tmp_dir) / "virtualenvs"),
+                "in-project": True,
+            }
+        }
+    )
+
+    mocker.patch(
+        "poetry.utils._compat.subprocess.check_output",
+        side_effect=check_output_wrapper(),
+    )
+    mocker.patch(
+        "poetry.utils._compat.subprocess.Popen.communicate",
+        side_effect=[("/prefix", None), ("/prefix", None)],
+    )
+    m = mocker.patch("poetry.utils.env.EnvManager.build_venv")
+
+    manager.activate("python3.7", NullIO())
+
+    m.assert_called_with(
+        os.path.join(str(poetry.file.parent), ".venv"), executable="python3.7"
+    )
+
+    envs_file = TomlFile(Path(tmp_dir) / "virtualenvs" / "envs.toml")
+    assert not envs_file.exists()