Repository: python-poetry/poetry

Issue:
Install command fails when extras_require.values() contains variables
<!--
  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**: Windows 7
- **Poetry version**: 0.12.10
- **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 🙌 ❤️ -->
When installing a dependency which is specifying extra dependencies via a variable, like
`extras_require={"test": tests_require, ...` (see [graphene](https://github.com/graphql-python/graphene/blob/master/setup.py)), you get an exception `[AttributeError], 'Name' object has no attribute 'elts'` from `poetry/utils/setup_reader.py:285`. The code makes the implicit assumption that values in `extras_require` are lists.


Discussion:

Resolving patch:
diff --git a/poetry/utils/setup_reader.py b/poetry/utils/setup_reader.py
--- a/poetry/utils/setup_reader.py
+++ b/poetry/utils/setup_reader.py
@@ -282,7 +282,11 @@ def _find_extras_require(
 
         if isinstance(value, ast.Dict):
             for key, val in zip(value.keys, value.values):
-                extras_require[key.s] = [e.s for e in val.elts]
+                if isinstance(val, ast.Name):
+                    val = self._find_variable_in_body(body, val.id)
+
+                if isinstance(val, ast.List):
+                    extras_require[key.s] = [e.s for e in val.elts]
         elif isinstance(value, ast.Name):
             variable = self._find_variable_in_body(body, value.id)
 
@@ -290,7 +294,11 @@ def _find_extras_require(
                 return extras_require
 
             for key, val in zip(variable.keys, variable.values):
-                extras_require[key.s] = [e.s for e in val.elts]
+                if isinstance(val, ast.Name):
+                    val = self._find_variable_in_body(body, val.id)
+
+                if isinstance(val, ast.List):
+                    extras_require[key.s] = [e.s for e in val.elts]
 
         return extras_require
 


Test patch:
diff --git a/tests/utils/fixtures/setups/extras_require_with_vars/setup.py b/tests/utils/fixtures/setups/extras_require_with_vars/setup.py
new file mode 100644
--- /dev/null
+++ b/tests/utils/fixtures/setups/extras_require_with_vars/setup.py
@@ -0,0 +1,11 @@
+from setuptools import setup
+
+tests_require = ["pytest"]
+
+setup(
+    name="extras_require_with_vars",
+    version="0.0.1",
+    description="test setup_reader.py",
+    install_requires=[],
+    extras_require={"test": tests_require},
+)
diff --git a/tests/utils/test_setup_reader.py b/tests/utils/test_setup_reader.py
--- a/tests/utils/test_setup_reader.py
+++ b/tests/utils/test_setup_reader.py
@@ -149,3 +149,20 @@ def test_setup_reader_read_setup_call_in_main(setup):
     assert expected_install_requires == result["install_requires"]
     assert expected_extras_require == result["extras_require"]
     assert expected_python_requires == result["python_requires"]
+
+
+@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4")
+def test_setup_reader_read_extras_require_with_variables(setup):
+    result = SetupReader.read_from_directory(setup("extras_require_with_vars"))
+
+    expected_name = "extras_require_with_vars"
+    expected_version = "0.0.1"
+    expected_install_requires = []
+    expected_extras_require = {"test": ["pytest"]}
+    expected_python_requires = None
+
+    assert expected_name == result["name"]
+    assert expected_version == result["version"]
+    assert expected_install_requires == result["install_requires"]
+    assert expected_extras_require == result["extras_require"]
+    assert expected_python_requires == result["python_requires"]