We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN 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.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `poetry/utils/setup_reader.py`
Content:
```
1 import ast
2 
3 try:
4     from configparser import ConfigParser
5 except ImportError:
6     from ConfigParser import ConfigParser
7 
8 from typing import Any
9 from typing import Dict
10 from typing import Iterable
11 from typing import List
12 from typing import Optional
13 from typing import Tuple
14 from typing import Union
15 
16 from ._compat import PY35
17 from ._compat import basestring
18 from ._compat import Path
19 
20 
21 class SetupReader(object):
22     """
23     Class that reads a setup.py file without executing it.
24     """
25 
26     DEFAULT = {
27         "name": None,
28         "version": None,
29         "install_requires": [],
30         "extras_require": {},
31         "python_requires": None,
32     }
33 
34     FILES = ["setup.py", "setup.cfg"]
35 
36     @classmethod
37     def read_from_directory(
38         cls, directory
39     ):  # type: (Union[basestring, Path]) -> Dict[str, Union[List, Dict]]
40         if isinstance(directory, basestring):
41             directory = Path(directory)
42 
43         result = cls.DEFAULT.copy()
44         for filename in cls.FILES:
45             filepath = directory / filename
46             if not filepath.exists():
47                 continue
48 
49             new_result = getattr(cls(), "read_{}".format(filename.replace(".", "_")))(
50                 filepath
51             )
52 
53             for key in result.keys():
54                 if new_result[key]:
55                     result[key] = new_result[key]
56 
57         return result
58 
59     @classmethod
60     def _is_empty_result(cls, result):  # type: (Dict[str, Any]) -> bool
61         return (
62             not result["install_requires"]
63             and not result["extras_require"]
64             and not result["python_requires"]
65         )
66 
67     def read_setup_py(
68         self, filepath
69     ):  # type: (Union[basestring, Path]) -> Dict[str, Union[List, Dict]]
70         if not PY35:
71             return self.DEFAULT
72 
73         if isinstance(filepath, basestring):
74             filepath = Path(filepath)
75 
76         with filepath.open(encoding="utf-8") as f:
77             content = f.read()
78 
79         result = {}
80 
81         body = ast.parse(content).body
82 
83         setup_call, body = self._find_setup_call(body)
84         if not setup_call:
85             return self.DEFAULT
86 
87         # Inspecting keyword arguments
88         result["name"] = self._find_single_string(setup_call, body, "name")
89         result["version"] = self._find_single_string(setup_call, body, "version")
90         result["install_requires"] = self._find_install_requires(setup_call, body)
91         result["extras_require"] = self._find_extras_require(setup_call, body)
92         result["python_requires"] = self._find_single_string(
93             setup_call, body, "python_requires"
94         )
95 
96         return result
97 
98     def read_setup_cfg(
99         self, filepath
100     ):  # type: (Union[basestring, Path]) -> Dict[str, Union[List, Dict]]
101         parser = ConfigParser()
102 
103         parser.read(str(filepath))
104 
105         name = None
106         version = None
107         if parser.has_option("metadata", "name"):
108             name = parser.get("metadata", "name")
109 
110         if parser.has_option("metadata", "version"):
111             version = parser.get("metadata", "version")
112 
113         install_requires = []
114         extras_require = {}
115         python_requires = None
116         if parser.has_section("options"):
117             if parser.has_option("options", "install_requires"):
118                 for dep in parser.get("options", "install_requires").split("\n"):
119                     dep = dep.strip()
120                     if not dep:
121                         continue
122 
123                     install_requires.append(dep)
124 
125             if parser.has_option("options", "python_requires"):
126                 python_requires = parser.get("options", "python_requires")
127 
128         if parser.has_section("options.extras_require"):
129             for group in parser.options("options.extras_require"):
130                 extras_require[group] = []
131                 deps = parser.get("options.extras_require", group)
132                 for dep in deps.split("\n"):
133                     dep = dep.strip()
134                     if not dep:
135                         continue
136 
137                     extras_require[group].append(dep)
138 
139         return {
140             "name": name,
141             "version": version,
142             "install_requires": install_requires,
143             "extras_require": extras_require,
144             "python_requires": python_requires,
145         }
146 
147     def _find_setup_call(
148         self, elements
149     ):  # type: (List[Any]) -> Tuple[Optional[ast.Call], Optional[List[Any]]]
150         funcdefs = []
151         for i, element in enumerate(elements):
152             if isinstance(element, ast.If) and i == len(elements) - 1:
153                 # Checking if the last element is an if statement
154                 # and if it is 'if __name__ == "__main__"' which
155                 # could contain the call to setup()
156                 test = element.test
157                 if not isinstance(test, ast.Compare):
158                     continue
159 
160                 left = test.left
161                 if not isinstance(left, ast.Name):
162                     continue
163 
164                 if left.id != "__name__":
165                     continue
166 
167                 setup_call, body = self._find_sub_setup_call([element])
168                 if not setup_call:
169                     continue
170 
171                 return setup_call, body + elements
172             if not isinstance(element, ast.Expr):
173                 if isinstance(element, ast.FunctionDef):
174                     funcdefs.append(element)
175 
176                 continue
177 
178             value = element.value
179             if not isinstance(value, ast.Call):
180                 continue
181 
182             func = value.func
183             if not isinstance(func, ast.Name):
184                 continue
185 
186             if func.id != "setup":
187                 continue
188 
189             return value, elements
190 
191         # Nothing, we inspect the function definitions
192         return self._find_sub_setup_call(funcdefs)
193 
194     def _find_sub_setup_call(
195         self, elements
196     ):  # type: (List[Any]) -> Tuple[Optional[ast.Call], Optional[List[Any]]]
197         for element in elements:
198             if not isinstance(element, (ast.FunctionDef, ast.If)):
199                 continue
200 
201             setup_call = self._find_setup_call(element.body)
202             if setup_call != (None, None):
203                 setup_call, body = setup_call
204 
205                 body = elements + body
206 
207                 return setup_call, body
208 
209         return None, None
210 
211     def _find_install_requires(
212         self, call, body
213     ):  # type: (ast.Call, Iterable[Any]) -> List[str]
214         install_requires = []
215         value = self._find_in_call(call, "install_requires")
216         if value is None:
217             # Trying to find in kwargs
218             kwargs = self._find_call_kwargs(call)
219 
220             if kwargs is None or not isinstance(kwargs, ast.Name):
221                 return install_requires
222 
223             variable = self._find_variable_in_body(body, kwargs.id)
224             if not isinstance(variable, (ast.Dict, ast.Call)):
225                 return install_requires
226 
227             if isinstance(variable, ast.Call):
228                 if not isinstance(variable.func, ast.Name):
229                     return install_requires
230 
231                 if variable.func.id != "dict":
232                     return install_requires
233 
234                 value = self._find_in_call(variable, "install_requires")
235             else:
236                 value = self._find_in_dict(variable, "install_requires")
237 
238         if value is None:
239             return install_requires
240 
241         if isinstance(value, ast.List):
242             for el in value.elts:
243                 install_requires.append(el.s)
244         elif isinstance(value, ast.Name):
245             variable = self._find_variable_in_body(body, value.id)
246 
247             if variable is not None and isinstance(variable, ast.List):
248                 for el in variable.elts:
249                     install_requires.append(el.s)
250 
251         return install_requires
252 
253     def _find_extras_require(
254         self, call, body
255     ):  # type: (ast.Call, Iterable[Any]) -> Dict[str, List]
256         extras_require = {}
257         value = self._find_in_call(call, "extras_require")
258         if value is None:
259             # Trying to find in kwargs
260             kwargs = self._find_call_kwargs(call)
261 
262             if kwargs is None or not isinstance(kwargs, ast.Name):
263                 return extras_require
264 
265             variable = self._find_variable_in_body(body, kwargs.id)
266             if not isinstance(variable, (ast.Dict, ast.Call)):
267                 return extras_require
268 
269             if isinstance(variable, ast.Call):
270                 if not isinstance(variable.func, ast.Name):
271                     return extras_require
272 
273                 if variable.func.id != "dict":
274                     return extras_require
275 
276                 value = self._find_in_call(variable, "extras_require")
277             else:
278                 value = self._find_in_dict(variable, "extras_require")
279 
280         if value is None:
281             return extras_require
282 
283         if isinstance(value, ast.Dict):
284             for key, val in zip(value.keys, value.values):
285                 extras_require[key.s] = [e.s for e in val.elts]
286         elif isinstance(value, ast.Name):
287             variable = self._find_variable_in_body(body, value.id)
288 
289             if variable is None or not isinstance(variable, ast.Dict):
290                 return extras_require
291 
292             for key, val in zip(variable.keys, variable.values):
293                 extras_require[key.s] = [e.s for e in val.elts]
294 
295         return extras_require
296 
297     def _find_single_string(
298         self, call, body, name
299     ):  # type: (ast.Call, List[Any], str) -> Optional[str]
300         value = self._find_in_call(call, name)
301         if value is None:
302             # Trying to find in kwargs
303             kwargs = self._find_call_kwargs(call)
304 
305             if kwargs is None or not isinstance(kwargs, ast.Name):
306                 return
307 
308             variable = self._find_variable_in_body(body, kwargs.id)
309             if not isinstance(variable, (ast.Dict, ast.Call)):
310                 return
311 
312             if isinstance(variable, ast.Call):
313                 if not isinstance(variable.func, ast.Name):
314                     return
315 
316                 if variable.func.id != "dict":
317                     return
318 
319                 value = self._find_in_call(variable, name)
320             else:
321                 value = self._find_in_dict(variable, name)
322 
323         if value is None:
324             return
325 
326         if isinstance(value, ast.Str):
327             return value.s
328         elif isinstance(value, ast.Name):
329             variable = self._find_variable_in_body(body, value.id)
330 
331             if variable is not None and isinstance(variable, ast.Str):
332                 return variable.s
333 
334     def _find_in_call(self, call, name):  # type: (ast.Call, str) -> Optional[Any]
335         for keyword in call.keywords:
336             if keyword.arg == name:
337                 return keyword.value
338 
339     def _find_call_kwargs(self, call):  # type: (ast.Call) -> Optional[Any]
340         kwargs = None
341         for keyword in call.keywords:
342             if keyword.arg is None:
343                 kwargs = keyword.value
344 
345         return kwargs
346 
347     def _find_variable_in_body(
348         self, body, name
349     ):  # type: (Iterable[Any], str) -> Optional[Any]
350         found = None
351         for elem in body:
352             if found:
353                 break
354 
355             if not isinstance(elem, ast.Assign):
356                 continue
357 
358             for target in elem.targets:
359                 if not isinstance(target, ast.Name):
360                     continue
361 
362                 if target.id == name:
363                     return elem.value
364 
365     def _find_in_dict(self, dict_, name):  # type: (ast.Call, str) -> Optional[Any]
366         for key, val in zip(dict_.keys, dict_.values):
367             if isinstance(key, ast.Str) and key.s == name:
368                 return val
369
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

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 @@
 
         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 @@
                 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

{"golden_diff": "diff --git a/poetry/utils/setup_reader.py b/poetry/utils/setup_reader.py\n--- a/poetry/utils/setup_reader.py\n+++ b/poetry/utils/setup_reader.py\n@@ -282,7 +282,11 @@\n \n         if isinstance(value, ast.Dict):\n             for key, val in zip(value.keys, value.values):\n-                extras_require[key.s] = [e.s for e in val.elts]\n+                if isinstance(val, ast.Name):\n+                    val = self._find_variable_in_body(body, val.id)\n+\n+                if isinstance(val, ast.List):\n+                    extras_require[key.s] = [e.s for e in val.elts]\n         elif isinstance(value, ast.Name):\n             variable = self._find_variable_in_body(body, value.id)\n \n@@ -290,7 +294,11 @@\n                 return extras_require\n \n             for key, val in zip(variable.keys, variable.values):\n-                extras_require[key.s] = [e.s for e in val.elts]\n+                if isinstance(val, ast.Name):\n+                    val = self._find_variable_in_body(body, val.id)\n+\n+                if isinstance(val, ast.List):\n+                    extras_require[key.s] = [e.s for e in val.elts]\n \n         return extras_require\n", "issue": "Install command fails when extras_require.values() contains variables\n<!--\r\n  Hi there! Thank you for discovering and submitting an issue.\r\n\r\n  Before you submit this; let's make sure of a few things.\r\n  Please make sure the following boxes are ticked if they are correct.\r\n  If not, please try and fulfill these first.\r\n-->\r\n\r\n<!-- Checked checkbox should look like this: [x] -->\r\n- [x] I am on the [latest](https://github.com/sdispater/poetry/releases/latest) Poetry version.\r\n- [x] I have searched the [issues](https://github.com/sdispater/poetry/issues) of this repo and believe that this is not a duplicate.\r\n- [x] If an exception occurs when executing a command, I executed it again in debug mode (`-vvv` option).\r\n\r\n<!--\r\n  Once those are done, if you're able to fill in the following list with your information,\r\n  it'd be very helpful to whoever handles the issue.\r\n-->\r\n\r\n- **OS version and name**: Windows 7\r\n- **Poetry version**: 0.12.10\r\n- **Link of a [Gist](https://gist.github.com/) with the contents of your pyproject.toml file**: n/a\r\n## Issue\r\n<!-- Now feel free to write your issue, but please be descriptive! Thanks again \ud83d\ude4c \u2764\ufe0f -->\r\nWhen installing a dependency which is specifying extra dependencies via a variable, like\r\n`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.\n", "before_files": [{"content": "import ast\n\ntry:\n    from configparser import ConfigParser\nexcept ImportError:\n    from ConfigParser import ConfigParser\n\nfrom typing import Any\nfrom typing import Dict\nfrom typing import Iterable\nfrom typing import List\nfrom typing import Optional\nfrom typing import Tuple\nfrom typing import Union\n\nfrom ._compat import PY35\nfrom ._compat import basestring\nfrom ._compat import Path\n\n\nclass SetupReader(object):\n    \"\"\"\n    Class that reads a setup.py file without executing it.\n    \"\"\"\n\n    DEFAULT = {\n        \"name\": None,\n        \"version\": None,\n        \"install_requires\": [],\n        \"extras_require\": {},\n        \"python_requires\": None,\n    }\n\n    FILES = [\"setup.py\", \"setup.cfg\"]\n\n    @classmethod\n    def read_from_directory(\n        cls, directory\n    ):  # type: (Union[basestring, Path]) -> Dict[str, Union[List, Dict]]\n        if isinstance(directory, basestring):\n            directory = Path(directory)\n\n        result = cls.DEFAULT.copy()\n        for filename in cls.FILES:\n            filepath = directory / filename\n            if not filepath.exists():\n                continue\n\n            new_result = getattr(cls(), \"read_{}\".format(filename.replace(\".\", \"_\")))(\n                filepath\n            )\n\n            for key in result.keys():\n                if new_result[key]:\n                    result[key] = new_result[key]\n\n        return result\n\n    @classmethod\n    def _is_empty_result(cls, result):  # type: (Dict[str, Any]) -> bool\n        return (\n            not result[\"install_requires\"]\n            and not result[\"extras_require\"]\n            and not result[\"python_requires\"]\n        )\n\n    def read_setup_py(\n        self, filepath\n    ):  # type: (Union[basestring, Path]) -> Dict[str, Union[List, Dict]]\n        if not PY35:\n            return self.DEFAULT\n\n        if isinstance(filepath, basestring):\n            filepath = Path(filepath)\n\n        with filepath.open(encoding=\"utf-8\") as f:\n            content = f.read()\n\n        result = {}\n\n        body = ast.parse(content).body\n\n        setup_call, body = self._find_setup_call(body)\n        if not setup_call:\n            return self.DEFAULT\n\n        # Inspecting keyword arguments\n        result[\"name\"] = self._find_single_string(setup_call, body, \"name\")\n        result[\"version\"] = self._find_single_string(setup_call, body, \"version\")\n        result[\"install_requires\"] = self._find_install_requires(setup_call, body)\n        result[\"extras_require\"] = self._find_extras_require(setup_call, body)\n        result[\"python_requires\"] = self._find_single_string(\n            setup_call, body, \"python_requires\"\n        )\n\n        return result\n\n    def read_setup_cfg(\n        self, filepath\n    ):  # type: (Union[basestring, Path]) -> Dict[str, Union[List, Dict]]\n        parser = ConfigParser()\n\n        parser.read(str(filepath))\n\n        name = None\n        version = None\n        if parser.has_option(\"metadata\", \"name\"):\n            name = parser.get(\"metadata\", \"name\")\n\n        if parser.has_option(\"metadata\", \"version\"):\n            version = parser.get(\"metadata\", \"version\")\n\n        install_requires = []\n        extras_require = {}\n        python_requires = None\n        if parser.has_section(\"options\"):\n            if parser.has_option(\"options\", \"install_requires\"):\n                for dep in parser.get(\"options\", \"install_requires\").split(\"\\n\"):\n                    dep = dep.strip()\n                    if not dep:\n                        continue\n\n                    install_requires.append(dep)\n\n            if parser.has_option(\"options\", \"python_requires\"):\n                python_requires = parser.get(\"options\", \"python_requires\")\n\n        if parser.has_section(\"options.extras_require\"):\n            for group in parser.options(\"options.extras_require\"):\n                extras_require[group] = []\n                deps = parser.get(\"options.extras_require\", group)\n                for dep in deps.split(\"\\n\"):\n                    dep = dep.strip()\n                    if not dep:\n                        continue\n\n                    extras_require[group].append(dep)\n\n        return {\n            \"name\": name,\n            \"version\": version,\n            \"install_requires\": install_requires,\n            \"extras_require\": extras_require,\n            \"python_requires\": python_requires,\n        }\n\n    def _find_setup_call(\n        self, elements\n    ):  # type: (List[Any]) -> Tuple[Optional[ast.Call], Optional[List[Any]]]\n        funcdefs = []\n        for i, element in enumerate(elements):\n            if isinstance(element, ast.If) and i == len(elements) - 1:\n                # Checking if the last element is an if statement\n                # and if it is 'if __name__ == \"__main__\"' which\n                # could contain the call to setup()\n                test = element.test\n                if not isinstance(test, ast.Compare):\n                    continue\n\n                left = test.left\n                if not isinstance(left, ast.Name):\n                    continue\n\n                if left.id != \"__name__\":\n                    continue\n\n                setup_call, body = self._find_sub_setup_call([element])\n                if not setup_call:\n                    continue\n\n                return setup_call, body + elements\n            if not isinstance(element, ast.Expr):\n                if isinstance(element, ast.FunctionDef):\n                    funcdefs.append(element)\n\n                continue\n\n            value = element.value\n            if not isinstance(value, ast.Call):\n                continue\n\n            func = value.func\n            if not isinstance(func, ast.Name):\n                continue\n\n            if func.id != \"setup\":\n                continue\n\n            return value, elements\n\n        # Nothing, we inspect the function definitions\n        return self._find_sub_setup_call(funcdefs)\n\n    def _find_sub_setup_call(\n        self, elements\n    ):  # type: (List[Any]) -> Tuple[Optional[ast.Call], Optional[List[Any]]]\n        for element in elements:\n            if not isinstance(element, (ast.FunctionDef, ast.If)):\n                continue\n\n            setup_call = self._find_setup_call(element.body)\n            if setup_call != (None, None):\n                setup_call, body = setup_call\n\n                body = elements + body\n\n                return setup_call, body\n\n        return None, None\n\n    def _find_install_requires(\n        self, call, body\n    ):  # type: (ast.Call, Iterable[Any]) -> List[str]\n        install_requires = []\n        value = self._find_in_call(call, \"install_requires\")\n        if value is None:\n            # Trying to find in kwargs\n            kwargs = self._find_call_kwargs(call)\n\n            if kwargs is None or not isinstance(kwargs, ast.Name):\n                return install_requires\n\n            variable = self._find_variable_in_body(body, kwargs.id)\n            if not isinstance(variable, (ast.Dict, ast.Call)):\n                return install_requires\n\n            if isinstance(variable, ast.Call):\n                if not isinstance(variable.func, ast.Name):\n                    return install_requires\n\n                if variable.func.id != \"dict\":\n                    return install_requires\n\n                value = self._find_in_call(variable, \"install_requires\")\n            else:\n                value = self._find_in_dict(variable, \"install_requires\")\n\n        if value is None:\n            return install_requires\n\n        if isinstance(value, ast.List):\n            for el in value.elts:\n                install_requires.append(el.s)\n        elif isinstance(value, ast.Name):\n            variable = self._find_variable_in_body(body, value.id)\n\n            if variable is not None and isinstance(variable, ast.List):\n                for el in variable.elts:\n                    install_requires.append(el.s)\n\n        return install_requires\n\n    def _find_extras_require(\n        self, call, body\n    ):  # type: (ast.Call, Iterable[Any]) -> Dict[str, List]\n        extras_require = {}\n        value = self._find_in_call(call, \"extras_require\")\n        if value is None:\n            # Trying to find in kwargs\n            kwargs = self._find_call_kwargs(call)\n\n            if kwargs is None or not isinstance(kwargs, ast.Name):\n                return extras_require\n\n            variable = self._find_variable_in_body(body, kwargs.id)\n            if not isinstance(variable, (ast.Dict, ast.Call)):\n                return extras_require\n\n            if isinstance(variable, ast.Call):\n                if not isinstance(variable.func, ast.Name):\n                    return extras_require\n\n                if variable.func.id != \"dict\":\n                    return extras_require\n\n                value = self._find_in_call(variable, \"extras_require\")\n            else:\n                value = self._find_in_dict(variable, \"extras_require\")\n\n        if value is None:\n            return extras_require\n\n        if isinstance(value, ast.Dict):\n            for key, val in zip(value.keys, value.values):\n                extras_require[key.s] = [e.s for e in val.elts]\n        elif isinstance(value, ast.Name):\n            variable = self._find_variable_in_body(body, value.id)\n\n            if variable is None or not isinstance(variable, ast.Dict):\n                return extras_require\n\n            for key, val in zip(variable.keys, variable.values):\n                extras_require[key.s] = [e.s for e in val.elts]\n\n        return extras_require\n\n    def _find_single_string(\n        self, call, body, name\n    ):  # type: (ast.Call, List[Any], str) -> Optional[str]\n        value = self._find_in_call(call, name)\n        if value is None:\n            # Trying to find in kwargs\n            kwargs = self._find_call_kwargs(call)\n\n            if kwargs is None or not isinstance(kwargs, ast.Name):\n                return\n\n            variable = self._find_variable_in_body(body, kwargs.id)\n            if not isinstance(variable, (ast.Dict, ast.Call)):\n                return\n\n            if isinstance(variable, ast.Call):\n                if not isinstance(variable.func, ast.Name):\n                    return\n\n                if variable.func.id != \"dict\":\n                    return\n\n                value = self._find_in_call(variable, name)\n            else:\n                value = self._find_in_dict(variable, name)\n\n        if value is None:\n            return\n\n        if isinstance(value, ast.Str):\n            return value.s\n        elif isinstance(value, ast.Name):\n            variable = self._find_variable_in_body(body, value.id)\n\n            if variable is not None and isinstance(variable, ast.Str):\n                return variable.s\n\n    def _find_in_call(self, call, name):  # type: (ast.Call, str) -> Optional[Any]\n        for keyword in call.keywords:\n            if keyword.arg == name:\n                return keyword.value\n\n    def _find_call_kwargs(self, call):  # type: (ast.Call) -> Optional[Any]\n        kwargs = None\n        for keyword in call.keywords:\n            if keyword.arg is None:\n                kwargs = keyword.value\n\n        return kwargs\n\n    def _find_variable_in_body(\n        self, body, name\n    ):  # type: (Iterable[Any], str) -> Optional[Any]\n        found = None\n        for elem in body:\n            if found:\n                break\n\n            if not isinstance(elem, ast.Assign):\n                continue\n\n            for target in elem.targets:\n                if not isinstance(target, ast.Name):\n                    continue\n\n                if target.id == name:\n                    return elem.value\n\n    def _find_in_dict(self, dict_, name):  # type: (ast.Call, str) -> Optional[Any]\n        for key, val in zip(dict_.keys, dict_.values):\n            if isinstance(key, ast.Str) and key.s == name:\n                return val\n", "path": "poetry/utils/setup_reader.py"}], "after_files": [{"content": "import ast\n\ntry:\n    from configparser import ConfigParser\nexcept ImportError:\n    from ConfigParser import ConfigParser\n\nfrom typing import Any\nfrom typing import Dict\nfrom typing import Iterable\nfrom typing import List\nfrom typing import Optional\nfrom typing import Tuple\nfrom typing import Union\n\nfrom ._compat import PY35\nfrom ._compat import basestring\nfrom ._compat import Path\n\n\nclass SetupReader(object):\n    \"\"\"\n    Class that reads a setup.py file without executing it.\n    \"\"\"\n\n    DEFAULT = {\n        \"name\": None,\n        \"version\": None,\n        \"install_requires\": [],\n        \"extras_require\": {},\n        \"python_requires\": None,\n    }\n\n    FILES = [\"setup.py\", \"setup.cfg\"]\n\n    @classmethod\n    def read_from_directory(\n        cls, directory\n    ):  # type: (Union[basestring, Path]) -> Dict[str, Union[List, Dict]]\n        if isinstance(directory, basestring):\n            directory = Path(directory)\n\n        result = cls.DEFAULT.copy()\n        for filename in cls.FILES:\n            filepath = directory / filename\n            if not filepath.exists():\n                continue\n\n            new_result = getattr(cls(), \"read_{}\".format(filename.replace(\".\", \"_\")))(\n                filepath\n            )\n\n            for key in result.keys():\n                if new_result[key]:\n                    result[key] = new_result[key]\n\n        return result\n\n    @classmethod\n    def _is_empty_result(cls, result):  # type: (Dict[str, Any]) -> bool\n        return (\n            not result[\"install_requires\"]\n            and not result[\"extras_require\"]\n            and not result[\"python_requires\"]\n        )\n\n    def read_setup_py(\n        self, filepath\n    ):  # type: (Union[basestring, Path]) -> Dict[str, Union[List, Dict]]\n        if not PY35:\n            return self.DEFAULT\n\n        if isinstance(filepath, basestring):\n            filepath = Path(filepath)\n\n        with filepath.open(encoding=\"utf-8\") as f:\n            content = f.read()\n\n        result = {}\n\n        body = ast.parse(content).body\n\n        setup_call, body = self._find_setup_call(body)\n        if not setup_call:\n            return self.DEFAULT\n\n        # Inspecting keyword arguments\n        result[\"name\"] = self._find_single_string(setup_call, body, \"name\")\n        result[\"version\"] = self._find_single_string(setup_call, body, \"version\")\n        result[\"install_requires\"] = self._find_install_requires(setup_call, body)\n        result[\"extras_require\"] = self._find_extras_require(setup_call, body)\n        result[\"python_requires\"] = self._find_single_string(\n            setup_call, body, \"python_requires\"\n        )\n\n        return result\n\n    def read_setup_cfg(\n        self, filepath\n    ):  # type: (Union[basestring, Path]) -> Dict[str, Union[List, Dict]]\n        parser = ConfigParser()\n\n        parser.read(str(filepath))\n\n        name = None\n        version = None\n        if parser.has_option(\"metadata\", \"name\"):\n            name = parser.get(\"metadata\", \"name\")\n\n        if parser.has_option(\"metadata\", \"version\"):\n            version = parser.get(\"metadata\", \"version\")\n\n        install_requires = []\n        extras_require = {}\n        python_requires = None\n        if parser.has_section(\"options\"):\n            if parser.has_option(\"options\", \"install_requires\"):\n                for dep in parser.get(\"options\", \"install_requires\").split(\"\\n\"):\n                    dep = dep.strip()\n                    if not dep:\n                        continue\n\n                    install_requires.append(dep)\n\n            if parser.has_option(\"options\", \"python_requires\"):\n                python_requires = parser.get(\"options\", \"python_requires\")\n\n        if parser.has_section(\"options.extras_require\"):\n            for group in parser.options(\"options.extras_require\"):\n                extras_require[group] = []\n                deps = parser.get(\"options.extras_require\", group)\n                for dep in deps.split(\"\\n\"):\n                    dep = dep.strip()\n                    if not dep:\n                        continue\n\n                    extras_require[group].append(dep)\n\n        return {\n            \"name\": name,\n            \"version\": version,\n            \"install_requires\": install_requires,\n            \"extras_require\": extras_require,\n            \"python_requires\": python_requires,\n        }\n\n    def _find_setup_call(\n        self, elements\n    ):  # type: (List[Any]) -> Tuple[Optional[ast.Call], Optional[List[Any]]]\n        funcdefs = []\n        for i, element in enumerate(elements):\n            if isinstance(element, ast.If) and i == len(elements) - 1:\n                # Checking if the last element is an if statement\n                # and if it is 'if __name__ == \"__main__\"' which\n                # could contain the call to setup()\n                test = element.test\n                if not isinstance(test, ast.Compare):\n                    continue\n\n                left = test.left\n                if not isinstance(left, ast.Name):\n                    continue\n\n                if left.id != \"__name__\":\n                    continue\n\n                setup_call, body = self._find_sub_setup_call([element])\n                if not setup_call:\n                    continue\n\n                return setup_call, body + elements\n            if not isinstance(element, ast.Expr):\n                if isinstance(element, ast.FunctionDef):\n                    funcdefs.append(element)\n\n                continue\n\n            value = element.value\n            if not isinstance(value, ast.Call):\n                continue\n\n            func = value.func\n            if not isinstance(func, ast.Name):\n                continue\n\n            if func.id != \"setup\":\n                continue\n\n            return value, elements\n\n        # Nothing, we inspect the function definitions\n        return self._find_sub_setup_call(funcdefs)\n\n    def _find_sub_setup_call(\n        self, elements\n    ):  # type: (List[Any]) -> Tuple[Optional[ast.Call], Optional[List[Any]]]\n        for element in elements:\n            if not isinstance(element, (ast.FunctionDef, ast.If)):\n                continue\n\n            setup_call = self._find_setup_call(element.body)\n            if setup_call != (None, None):\n                setup_call, body = setup_call\n\n                body = elements + body\n\n                return setup_call, body\n\n        return None, None\n\n    def _find_install_requires(\n        self, call, body\n    ):  # type: (ast.Call, Iterable[Any]) -> List[str]\n        install_requires = []\n        value = self._find_in_call(call, \"install_requires\")\n        if value is None:\n            # Trying to find in kwargs\n            kwargs = self._find_call_kwargs(call)\n\n            if kwargs is None or not isinstance(kwargs, ast.Name):\n                return install_requires\n\n            variable = self._find_variable_in_body(body, kwargs.id)\n            if not isinstance(variable, (ast.Dict, ast.Call)):\n                return install_requires\n\n            if isinstance(variable, ast.Call):\n                if not isinstance(variable.func, ast.Name):\n                    return install_requires\n\n                if variable.func.id != \"dict\":\n                    return install_requires\n\n                value = self._find_in_call(variable, \"install_requires\")\n            else:\n                value = self._find_in_dict(variable, \"install_requires\")\n\n        if value is None:\n            return install_requires\n\n        if isinstance(value, ast.List):\n            for el in value.elts:\n                install_requires.append(el.s)\n        elif isinstance(value, ast.Name):\n            variable = self._find_variable_in_body(body, value.id)\n\n            if variable is not None and isinstance(variable, ast.List):\n                for el in variable.elts:\n                    install_requires.append(el.s)\n\n        return install_requires\n\n    def _find_extras_require(\n        self, call, body\n    ):  # type: (ast.Call, Iterable[Any]) -> Dict[str, List]\n        extras_require = {}\n        value = self._find_in_call(call, \"extras_require\")\n        if value is None:\n            # Trying to find in kwargs\n            kwargs = self._find_call_kwargs(call)\n\n            if kwargs is None or not isinstance(kwargs, ast.Name):\n                return extras_require\n\n            variable = self._find_variable_in_body(body, kwargs.id)\n            if not isinstance(variable, (ast.Dict, ast.Call)):\n                return extras_require\n\n            if isinstance(variable, ast.Call):\n                if not isinstance(variable.func, ast.Name):\n                    return extras_require\n\n                if variable.func.id != \"dict\":\n                    return extras_require\n\n                value = self._find_in_call(variable, \"extras_require\")\n            else:\n                value = self._find_in_dict(variable, \"extras_require\")\n\n        if value is None:\n            return extras_require\n\n        if isinstance(value, ast.Dict):\n            for key, val in zip(value.keys, value.values):\n                if isinstance(val, ast.Name):\n                    val = self._find_variable_in_body(body, val.id)\n\n                if isinstance(val, ast.List):\n                    extras_require[key.s] = [e.s for e in val.elts]\n        elif isinstance(value, ast.Name):\n            variable = self._find_variable_in_body(body, value.id)\n\n            if variable is None or not isinstance(variable, ast.Dict):\n                return extras_require\n\n            for key, val in zip(variable.keys, variable.values):\n                if isinstance(val, ast.Name):\n                    val = self._find_variable_in_body(body, val.id)\n\n                if isinstance(val, ast.List):\n                    extras_require[key.s] = [e.s for e in val.elts]\n\n        return extras_require\n\n    def _find_single_string(\n        self, call, body, name\n    ):  # type: (ast.Call, List[Any], str) -> Optional[str]\n        value = self._find_in_call(call, name)\n        if value is None:\n            # Trying to find in kwargs\n            kwargs = self._find_call_kwargs(call)\n\n            if kwargs is None or not isinstance(kwargs, ast.Name):\n                return\n\n            variable = self._find_variable_in_body(body, kwargs.id)\n            if not isinstance(variable, (ast.Dict, ast.Call)):\n                return\n\n            if isinstance(variable, ast.Call):\n                if not isinstance(variable.func, ast.Name):\n                    return\n\n                if variable.func.id != \"dict\":\n                    return\n\n                value = self._find_in_call(variable, name)\n            else:\n                value = self._find_in_dict(variable, name)\n\n        if value is None:\n            return\n\n        if isinstance(value, ast.Str):\n            return value.s\n        elif isinstance(value, ast.Name):\n            variable = self._find_variable_in_body(body, value.id)\n\n            if variable is not None and isinstance(variable, ast.Str):\n                return variable.s\n\n    def _find_in_call(self, call, name):  # type: (ast.Call, str) -> Optional[Any]\n        for keyword in call.keywords:\n            if keyword.arg == name:\n                return keyword.value\n\n    def _find_call_kwargs(self, call):  # type: (ast.Call) -> Optional[Any]\n        kwargs = None\n        for keyword in call.keywords:\n            if keyword.arg is None:\n                kwargs = keyword.value\n\n        return kwargs\n\n    def _find_variable_in_body(\n        self, body, name\n    ):  # type: (Iterable[Any], str) -> Optional[Any]\n        found = None\n        for elem in body:\n            if found:\n                break\n\n            if not isinstance(elem, ast.Assign):\n                continue\n\n            for target in elem.targets:\n                if not isinstance(target, ast.Name):\n                    continue\n\n                if target.id == name:\n                    return elem.value\n\n    def _find_in_dict(self, dict_, name):  # type: (ast.Call, str) -> Optional[Any]\n        for key, val in zip(dict_.keys, dict_.values):\n            if isinstance(key, ast.Str) and key.s == name:\n                return val\n", "path": "poetry/utils/setup_reader.py"}]}