Repository: python-poetry/poetry

Issue:
RecursionError when installing mkdocs-material
<!-- Checked checkbox should look like this: [x] -->
- [x] I am on the [latest](https://github.com/python-poetry/poetry/releases/latest) Poetry version.
- [x] I have searched the [issues](https://github.com/python-poetry/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).

- **OS version and name**: Windows 10 Pro 1909 18363.719
- **Poetry version**:  1.0.5
- **Link of a [Gist](https://gist.github.com/) with the contents of your pyproject.toml file**: https://gist.github.com/ndevln/f27b5b64251241d40e1b960ec96b8b68

## Issue
Hi,

when running: `poetry install -E docs_material` I get the following error:

```
[RecursionError]
maximum recursion depth exceeded
```

There are quite a few similar issues (like #2329), but it seems that this problem is package specific?!?

Thank you for poetry and your effort :)





Discussion:
Could be fixed by #2342 and #2361. I can test it when these PR are part of the pre-release version.
The problem persists in 1.1.0a2 (and 1.0.9). 

```
Installing dependencies from lock file

  RecursionError

  maximum recursion depth exceeded

  at ~\.poetry\lib\poetry\utils\extras.py:47 in <genexpr>
      43│             if package:
      44│                 yield package.name
      45│                 # Recurse for dependencies
      46│                 for dependency_package_name in _extra_packages(
    → 47│                     dependency.name for dependency in package.requires
      48│                 ):
      49│                     yield dependency_package_name
      50│
      51│     return _extra_packages(extra_package_names)

```
@ndevln When using 1.1.0a2 did you regenerate your lock file?
I did not.

I tried again with 1) regenerating the lock file, 2) deleting the lock file and creating it new. For my last test I used the minimal example from the gist above and created a new virtual environment. But still all 3 tests resulted in the same error. Sry

Resolving patch:
diff --git a/poetry/utils/extras.py b/poetry/utils/extras.py
--- a/poetry/utils/extras.py
+++ b/poetry/utils/extras.py
@@ -33,6 +33,9 @@ def get_extra_package_names(
         for extra_package_name in extras.get(extra_name, ())
     ]
 
+    # keep record of packages seen during recursion in order to avoid recursion error
+    seen_package_names = set()
+
     def _extra_packages(package_names):
         """Recursively find dependencies for packages names"""
         # for each extra pacakge name
@@ -41,11 +44,16 @@ def _extra_packages(package_names):
             # dependency (like setuptools), which should be ignored
             package = packages_by_name.get(canonicalize_name(package_name))
             if package:
-                yield package.name
+                if package.name not in seen_package_names:
+                    seen_package_names.add(package.name)
+                    yield package.name
                 # Recurse for dependencies
                 for dependency_package_name in _extra_packages(
-                    dependency.name for dependency in package.requires
+                    dependency.name
+                    for dependency in package.requires
+                    if dependency.name not in seen_package_names
                 ):
+                    seen_package_names.add(dependency_package_name)
                     yield dependency_package_name
 
     return _extra_packages(extra_package_names)


Test patch:
diff --git a/tests/utils/test_extras.py b/tests/utils/test_extras.py
--- a/tests/utils/test_extras.py
+++ b/tests/utils/test_extras.py
@@ -9,6 +9,12 @@
 _PACKAGE_BAR = Package("bar", "0.3.0")
 _PACKAGE_BAR.add_dependency("foo")
 
+# recursive dependency
+_PACKAGE_BAZ = Package("baz", "0.4.0")
+_PACKAGE_BAZ.add_dependency("quix")
+_PACKAGE_QUIX = Package("quix", "0.5.0")
+_PACKAGE_QUIX.add_dependency("baz")
+
 
 @pytest.mark.parametrize(
     "packages,extras,extra_names,expected_extra_package_names",
@@ -40,6 +46,12 @@
             ["group0", "group1"],
             ["bar", "foo", "spam"],
         ),
+        (
+            [_PACKAGE_BAZ, _PACKAGE_QUIX],
+            {"group0": ["baz"], "group1": ["quix"]},
+            ["group0", "group1"],
+            ["baz", "quix"],
+        ),
     ],
 )
 def test_get_extra_package_names(