We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
`poetry add --extras security requests` fails with `ValueError`
- [ ] ~I am on the [latest](https://github.com/sdispater/poetry/releases/latest) Poetry version.~
- [x] I am on the [master](https://github.com/sdispater/poetry/tree/master) branch of Poetry.
- [x] It can be reproduced on the [latest beta version](https://github.com/sdispater/poetry/releases/tag/1.0.0b1) of Poetry.
- [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).

- **OS version and name**: `any`
- **Poetry version**: `1.0.0a0` — `1.0.0b1`
- **~Link of a [Gist](https://gist.github.com/) with the contents of your~ pyproject.toml file**: this bug could be reproduced with any `pyproject.toml`, so you can just generate a new one using `poetry init`.

## Issue
Using `poetry` from `master`/`develop` branches or any recent alpha/beta release, it's not possible to add the `requests` package with the `security` extra.

```bash
$ poetry -V
Poetry version 1.0.0b1
$ poetry add --extras security requests -v
Using virtualenv: /home/mim/.cache/pypoetry/virtualenvs/test-poetry-requests-security-XPJu19SV-py3.7
Using version ^2.22.0 for requests

Updating dependencies
Resolving dependencies... (0.1s)

[ValueError]
Array has mixed types elements

Traceback (most recent call last):
  File "/home/mim/.poetry/lib/poetry/_vendor/py3.7/clikit/console_application.py", line 132, in run
    status_code = command.handle(parsed_args, io)
  File "/home/mim/.poetry/lib/poetry/_vendor/py3.7/clikit/api/command/command.py", line 119, in handle
    status_code = self._do_handle(args, io)
  File "/home/mim/.poetry/lib/poetry/_vendor/py3.7/clikit/api/command/command.py", line 167, in _do_handle
    return getattr(handler, handler_method)(args, io, self)
  File "/home/mim/.poetry/lib/poetry/_vendor/py3.7/cleo/commands/command.py", line 92, in wrap_handle
    return self.handle()
  File "/home/mim/.poetry/lib/poetry/console/commands/add.py", line 149, in handle
    status = installer.run()
  File "/home/mim/.poetry/lib/poetry/installation/installer.py", line 74, in run
    self._do_install(local_repo)
  File "/home/mim/.poetry/lib/poetry/installation/installer.py", line 189, in _do_install
    self._write_lock_file(local_repo)
  File "/home/mim/.poetry/lib/poetry/installation/installer.py", line 290, in _write_lock_file
    updated_lock = self._locker.set_lock_data(self._package, repo.packages)
  File "/home/mim/.poetry/lib/poetry/packages/locker.py", line 149, in set_lock_data
    lock['package'] = packages
  File "/home/mim/.poetry/lib/poetry/_vendor/py3.7/tomlkit/container.py", line 551, in __setitem__
    self.append(key, value)
  File "/home/mim/.poetry/lib/poetry/_vendor/py3.7/tomlkit/container.py", line 91, in append
    item = _item(item)
  File "/home/mim/.poetry/lib/poetry/_vendor/py3.7/tomlkit/items.py", line 62, in item
    i = item(_v)
  File "/home/mim/.poetry/lib/poetry/_vendor/py3.7/tomlkit/items.py", line 46, in item
    val[k] = item(v, _parent=val)
  File "/home/mim/.poetry/lib/poetry/_vendor/py3.7/tomlkit/items.py", line 70, in item
    a.append(v)
  File "/home/mim/.poetry/lib/poetry/_vendor/py3.7/tomlkit/items.py", line 778, in append
    raise ValueError('Array has mixed types elements')
```

--- 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/packages/locker.py`
Content:
```
1 import json
2 import re
3 
4 import poetry.packages
5 import poetry.repositories
6 
7 from hashlib import sha256
8 from tomlkit import document
9 from typing import List
10 
11 from poetry.utils._compat import Path
12 from poetry.utils.toml_file import TomlFile
13 from poetry.version.markers import parse_marker
14 
15 
16 class Locker(object):
17 
18     _relevant_keys = ["dependencies", "dev-dependencies", "source", "extras"]
19 
20     def __init__(self, lock, local_config):  # type: (Path, dict) -> None
21         self._lock = TomlFile(lock)
22         self._local_config = local_config
23         self._lock_data = None
24         self._content_hash = self._get_content_hash()
25 
26     @property
27     def lock(self):  # type: () -> TomlFile
28         return self._lock
29 
30     @property
31     def lock_data(self):
32         if self._lock_data is None:
33             self._lock_data = self._get_lock_data()
34 
35         return self._lock_data
36 
37     def is_locked(self):  # type: () -> bool
38         """
39         Checks whether the locker has been locked (lockfile found).
40         """
41         if not self._lock.exists():
42             return False
43 
44         return "package" in self.lock_data
45 
46     def is_fresh(self):  # type: () -> bool
47         """
48         Checks whether the lock file is still up to date with the current hash.
49         """
50         lock = self._lock.read()
51         metadata = lock.get("metadata", {})
52 
53         if "content-hash" in metadata:
54             return self._content_hash == lock["metadata"]["content-hash"]
55 
56         return False
57 
58     def locked_repository(
59         self, with_dev_reqs=False
60     ):  # type: (bool) -> poetry.repositories.Repository
61         """
62         Searches and returns a repository of locked packages.
63         """
64         if not self.is_locked():
65             return poetry.repositories.Repository()
66 
67         lock_data = self.lock_data
68         packages = poetry.repositories.Repository()
69 
70         if with_dev_reqs:
71             locked_packages = lock_data["package"]
72         else:
73             locked_packages = [
74                 p for p in lock_data["package"] if p["category"] == "main"
75             ]
76 
77         if not locked_packages:
78             return packages
79 
80         for info in locked_packages:
81             package = poetry.packages.Package(
82                 info["name"], info["version"], info["version"]
83             )
84             package.description = info.get("description", "")
85             package.category = info["category"]
86             package.optional = info["optional"]
87             package.hashes = lock_data["metadata"]["hashes"][info["name"]]
88             package.python_versions = info["python-versions"]
89             extras = info.get("extras", {})
90             if extras:
91                 for name, deps in extras.items():
92                     package.extras[name] = []
93 
94                     for dep in deps:
95                         m = re.match(r"^(.+?)(?:\s+\((.+)\))?$", dep)
96                         dep_name = m.group(1)
97                         constraint = m.group(2) or "*"
98 
99                         package.extras[name].append(
100                             poetry.packages.Dependency(dep_name, constraint)
101                         )
102 
103             if "marker" in info:
104                 package.marker = parse_marker(info["marker"])
105             else:
106                 # Compatibility for old locks
107                 if "requirements" in info:
108                     dep = poetry.packages.Dependency("foo", "0.0.0")
109                     for name, value in info["requirements"].items():
110                         if name == "python":
111                             dep.python_versions = value
112                         elif name == "platform":
113                             dep.platform = value
114 
115                     split_dep = dep.to_pep_508(False).split(";")
116                     if len(split_dep) > 1:
117                         package.marker = parse_marker(split_dep[1].strip())
118 
119             for dep_name, constraint in info.get("dependencies", {}).items():
120                 if isinstance(constraint, list):
121                     for c in constraint:
122                         package.add_dependency(dep_name, c)
123 
124                     continue
125 
126                 package.add_dependency(dep_name, constraint)
127 
128             if "source" in info:
129                 package.source_type = info["source"]["type"]
130                 package.source_url = info["source"]["url"]
131                 package.source_reference = info["source"]["reference"]
132 
133             packages.add_package(package)
134 
135         return packages
136 
137     def set_lock_data(self, root, packages):  # type: (...) -> bool
138         hashes = {}
139         packages = self._lock_packages(packages)
140         # Retrieving hashes
141         for package in packages:
142             if package["name"] not in hashes:
143                 hashes[package["name"]] = []
144 
145             hashes[package["name"]] += package["hashes"]
146             del package["hashes"]
147 
148         lock = document()
149         lock["package"] = packages
150 
151         if root.extras:
152             lock["extras"] = {
153                 extra: [dep.pretty_name for dep in deps]
154                 for extra, deps in root.extras.items()
155             }
156 
157         lock["metadata"] = {
158             "python-versions": root.python_versions,
159             "content-hash": self._content_hash,
160             "hashes": hashes,
161         }
162 
163         if not self.is_locked() or lock != self.lock_data:
164             self._write_lock_data(lock)
165 
166             return True
167 
168         return False
169 
170     def _write_lock_data(self, data):
171         self.lock.write(data)
172 
173         # Checking lock file data consistency
174         if data != self.lock.read():
175             raise RuntimeError("Inconsistent lock file data.")
176 
177         self._lock_data = None
178 
179     def _get_content_hash(self):  # type: () -> str
180         """
181         Returns the sha256 hash of the sorted content of the pyproject file.
182         """
183         content = self._local_config
184 
185         relevant_content = {}
186         for key in self._relevant_keys:
187             relevant_content[key] = content.get(key)
188 
189         content_hash = sha256(
190             json.dumps(relevant_content, sort_keys=True).encode()
191         ).hexdigest()
192 
193         return content_hash
194 
195     def _get_lock_data(self):  # type: () -> dict
196         if not self._lock.exists():
197             raise RuntimeError("No lockfile found. Unable to read locked packages")
198 
199         return self._lock.read()
200 
201     def _lock_packages(
202         self, packages
203     ):  # type: (List['poetry.packages.Package']) -> list
204         locked = []
205 
206         for package in sorted(packages, key=lambda x: x.name):
207             spec = self._dump_package(package)
208 
209             locked.append(spec)
210 
211         return locked
212 
213     def _dump_package(self, package):  # type: (poetry.packages.Package) -> dict
214         dependencies = {}
215         for dependency in sorted(package.requires, key=lambda d: d.name):
216             if dependency.is_optional() and not dependency.is_activated():
217                 continue
218 
219             if dependency.pretty_name not in dependencies:
220                 dependencies[dependency.pretty_name] = []
221             else:
222                 # Ensure we don't have mixed types in the lock file
223                 for i, spec in enumerate(dependencies[dependency.pretty_name]):
224                     if not isinstance(spec, dict):
225                         dependencies[dependency.pretty_name][i] = {"version": spec}
226 
227             constraint = {"version": str(dependency.pretty_constraint)}
228 
229             if dependency.extras:
230                 constraint["extras"] = dependency.extras
231 
232             if dependency.is_optional():
233                 constraint["optional"] = True
234 
235             if not dependency.python_constraint.is_any():
236                 constraint["python"] = str(dependency.python_constraint)
237 
238             if len(constraint) == 1:
239                 dependencies[dependency.pretty_name].append(constraint["version"])
240             else:
241                 dependencies[dependency.pretty_name].append(constraint)
242 
243         data = {
244             "name": package.pretty_name,
245             "version": package.pretty_version,
246             "description": package.description or "",
247             "category": package.category,
248             "optional": package.optional,
249             "python-versions": package.python_versions,
250             "hashes": sorted(package.hashes),
251         }
252         if not package.marker.is_any():
253             data["marker"] = str(package.marker)
254 
255         if package.extras:
256             extras = {}
257             for name, deps in package.extras.items():
258                 extras[name] = [
259                     str(dep) if not dep.constraint.is_any() else dep.name
260                     for dep in deps
261                 ]
262 
263             data["extras"] = extras
264 
265         if dependencies:
266             for k, constraints in dependencies.items():
267                 if len(constraints) == 1:
268                     dependencies[k] = constraints[0]
269 
270             data["dependencies"] = dependencies
271 
272         if package.source_type:
273             data["source"] = {
274                 "type": package.source_type,
275                 "url": package.source_url,
276                 "reference": package.source_reference,
277             }
278 
279         return data
280
```
--- 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/packages/locker.py b/poetry/packages/locker.py
--- a/poetry/packages/locker.py
+++ b/poetry/packages/locker.py
@@ -218,11 +218,6 @@
 
             if dependency.pretty_name not in dependencies:
                 dependencies[dependency.pretty_name] = []
-            else:
-                # Ensure we don't have mixed types in the lock file
-                for i, spec in enumerate(dependencies[dependency.pretty_name]):
-                    if not isinstance(spec, dict):
-                        dependencies[dependency.pretty_name][i] = {"version": spec}
 
             constraint = {"version": str(dependency.pretty_constraint)}
 
@@ -235,10 +230,15 @@
             if not dependency.python_constraint.is_any():
                 constraint["python"] = str(dependency.python_constraint)
 
-            if len(constraint) == 1:
-                dependencies[dependency.pretty_name].append(constraint["version"])
-            else:
-                dependencies[dependency.pretty_name].append(constraint)
+            dependencies[dependency.pretty_name].append(constraint)
+
+        # All the constraints should have the same type,
+        # but we want to simplify them if it's possible
+        for dependency, constraints in tuple(dependencies.items()):
+            if all(len(constraint) == 1 for constraint in constraints):
+                dependencies[dependency] = [
+                    constraint["version"] for constraint in constraints
+                ]
 
         data = {
             "name": package.pretty_name,

{"golden_diff": "diff --git a/poetry/packages/locker.py b/poetry/packages/locker.py\n--- a/poetry/packages/locker.py\n+++ b/poetry/packages/locker.py\n@@ -218,11 +218,6 @@\n \n             if dependency.pretty_name not in dependencies:\n                 dependencies[dependency.pretty_name] = []\n-            else:\n-                # Ensure we don't have mixed types in the lock file\n-                for i, spec in enumerate(dependencies[dependency.pretty_name]):\n-                    if not isinstance(spec, dict):\n-                        dependencies[dependency.pretty_name][i] = {\"version\": spec}\n \n             constraint = {\"version\": str(dependency.pretty_constraint)}\n \n@@ -235,10 +230,15 @@\n             if not dependency.python_constraint.is_any():\n                 constraint[\"python\"] = str(dependency.python_constraint)\n \n-            if len(constraint) == 1:\n-                dependencies[dependency.pretty_name].append(constraint[\"version\"])\n-            else:\n-                dependencies[dependency.pretty_name].append(constraint)\n+            dependencies[dependency.pretty_name].append(constraint)\n+\n+        # All the constraints should have the same type,\n+        # but we want to simplify them if it's possible\n+        for dependency, constraints in tuple(dependencies.items()):\n+            if all(len(constraint) == 1 for constraint in constraints):\n+                dependencies[dependency] = [\n+                    constraint[\"version\"] for constraint in constraints\n+                ]\n \n         data = {\n             \"name\": package.pretty_name,\n", "issue": "`poetry add --extras security requests` fails with `ValueError`\n- [ ] ~I am on the [latest](https://github.com/sdispater/poetry/releases/latest) Poetry version.~\r\n- [x] I am on the [master](https://github.com/sdispater/poetry/tree/master) branch of Poetry.\r\n- [x] It can be reproduced on the [latest beta version](https://github.com/sdispater/poetry/releases/tag/1.0.0b1) of Poetry.\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- **OS version and name**: `any`\r\n- **Poetry version**: `1.0.0a0` \u2014 `1.0.0b1`\r\n- **~Link of a [Gist](https://gist.github.com/) with the contents of your~ pyproject.toml file**: this bug could be reproduced with any `pyproject.toml`, so you can just generate a new one using `poetry init`.\r\n\r\n## Issue\r\nUsing `poetry` from `master`/`develop` branches or any recent alpha/beta release, it's not possible to add the `requests` package with the `security` extra.\r\n\r\n```bash\r\n$ poetry -V\r\nPoetry version 1.0.0b1\r\n$ poetry add --extras security requests -v\r\nUsing virtualenv: /home/mim/.cache/pypoetry/virtualenvs/test-poetry-requests-security-XPJu19SV-py3.7\r\nUsing version ^2.22.0 for requests\r\n\r\nUpdating dependencies\r\nResolving dependencies... (0.1s)\r\n\r\n[ValueError]\r\nArray has mixed types elements\r\n\r\nTraceback (most recent call last):\r\n  File \"/home/mim/.poetry/lib/poetry/_vendor/py3.7/clikit/console_application.py\", line 132, in run\r\n    status_code = command.handle(parsed_args, io)\r\n  File \"/home/mim/.poetry/lib/poetry/_vendor/py3.7/clikit/api/command/command.py\", line 119, in handle\r\n    status_code = self._do_handle(args, io)\r\n  File \"/home/mim/.poetry/lib/poetry/_vendor/py3.7/clikit/api/command/command.py\", line 167, in _do_handle\r\n    return getattr(handler, handler_method)(args, io, self)\r\n  File \"/home/mim/.poetry/lib/poetry/_vendor/py3.7/cleo/commands/command.py\", line 92, in wrap_handle\r\n    return self.handle()\r\n  File \"/home/mim/.poetry/lib/poetry/console/commands/add.py\", line 149, in handle\r\n    status = installer.run()\r\n  File \"/home/mim/.poetry/lib/poetry/installation/installer.py\", line 74, in run\r\n    self._do_install(local_repo)\r\n  File \"/home/mim/.poetry/lib/poetry/installation/installer.py\", line 189, in _do_install\r\n    self._write_lock_file(local_repo)\r\n  File \"/home/mim/.poetry/lib/poetry/installation/installer.py\", line 290, in _write_lock_file\r\n    updated_lock = self._locker.set_lock_data(self._package, repo.packages)\r\n  File \"/home/mim/.poetry/lib/poetry/packages/locker.py\", line 149, in set_lock_data\r\n    lock['package'] = packages\r\n  File \"/home/mim/.poetry/lib/poetry/_vendor/py3.7/tomlkit/container.py\", line 551, in __setitem__\r\n    self.append(key, value)\r\n  File \"/home/mim/.poetry/lib/poetry/_vendor/py3.7/tomlkit/container.py\", line 91, in append\r\n    item = _item(item)\r\n  File \"/home/mim/.poetry/lib/poetry/_vendor/py3.7/tomlkit/items.py\", line 62, in item\r\n    i = item(_v)\r\n  File \"/home/mim/.poetry/lib/poetry/_vendor/py3.7/tomlkit/items.py\", line 46, in item\r\n    val[k] = item(v, _parent=val)\r\n  File \"/home/mim/.poetry/lib/poetry/_vendor/py3.7/tomlkit/items.py\", line 70, in item\r\n    a.append(v)\r\n  File \"/home/mim/.poetry/lib/poetry/_vendor/py3.7/tomlkit/items.py\", line 778, in append\r\n    raise ValueError('Array has mixed types elements')\r\n```\n", "before_files": [{"content": "import json\nimport re\n\nimport poetry.packages\nimport poetry.repositories\n\nfrom hashlib import sha256\nfrom tomlkit import document\nfrom typing import List\n\nfrom poetry.utils._compat import Path\nfrom poetry.utils.toml_file import TomlFile\nfrom poetry.version.markers import parse_marker\n\n\nclass Locker(object):\n\n    _relevant_keys = [\"dependencies\", \"dev-dependencies\", \"source\", \"extras\"]\n\n    def __init__(self, lock, local_config):  # type: (Path, dict) -> None\n        self._lock = TomlFile(lock)\n        self._local_config = local_config\n        self._lock_data = None\n        self._content_hash = self._get_content_hash()\n\n    @property\n    def lock(self):  # type: () -> TomlFile\n        return self._lock\n\n    @property\n    def lock_data(self):\n        if self._lock_data is None:\n            self._lock_data = self._get_lock_data()\n\n        return self._lock_data\n\n    def is_locked(self):  # type: () -> bool\n        \"\"\"\n        Checks whether the locker has been locked (lockfile found).\n        \"\"\"\n        if not self._lock.exists():\n            return False\n\n        return \"package\" in self.lock_data\n\n    def is_fresh(self):  # type: () -> bool\n        \"\"\"\n        Checks whether the lock file is still up to date with the current hash.\n        \"\"\"\n        lock = self._lock.read()\n        metadata = lock.get(\"metadata\", {})\n\n        if \"content-hash\" in metadata:\n            return self._content_hash == lock[\"metadata\"][\"content-hash\"]\n\n        return False\n\n    def locked_repository(\n        self, with_dev_reqs=False\n    ):  # type: (bool) -> poetry.repositories.Repository\n        \"\"\"\n        Searches and returns a repository of locked packages.\n        \"\"\"\n        if not self.is_locked():\n            return poetry.repositories.Repository()\n\n        lock_data = self.lock_data\n        packages = poetry.repositories.Repository()\n\n        if with_dev_reqs:\n            locked_packages = lock_data[\"package\"]\n        else:\n            locked_packages = [\n                p for p in lock_data[\"package\"] if p[\"category\"] == \"main\"\n            ]\n\n        if not locked_packages:\n            return packages\n\n        for info in locked_packages:\n            package = poetry.packages.Package(\n                info[\"name\"], info[\"version\"], info[\"version\"]\n            )\n            package.description = info.get(\"description\", \"\")\n            package.category = info[\"category\"]\n            package.optional = info[\"optional\"]\n            package.hashes = lock_data[\"metadata\"][\"hashes\"][info[\"name\"]]\n            package.python_versions = info[\"python-versions\"]\n            extras = info.get(\"extras\", {})\n            if extras:\n                for name, deps in extras.items():\n                    package.extras[name] = []\n\n                    for dep in deps:\n                        m = re.match(r\"^(.+?)(?:\\s+\\((.+)\\))?$\", dep)\n                        dep_name = m.group(1)\n                        constraint = m.group(2) or \"*\"\n\n                        package.extras[name].append(\n                            poetry.packages.Dependency(dep_name, constraint)\n                        )\n\n            if \"marker\" in info:\n                package.marker = parse_marker(info[\"marker\"])\n            else:\n                # Compatibility for old locks\n                if \"requirements\" in info:\n                    dep = poetry.packages.Dependency(\"foo\", \"0.0.0\")\n                    for name, value in info[\"requirements\"].items():\n                        if name == \"python\":\n                            dep.python_versions = value\n                        elif name == \"platform\":\n                            dep.platform = value\n\n                    split_dep = dep.to_pep_508(False).split(\";\")\n                    if len(split_dep) > 1:\n                        package.marker = parse_marker(split_dep[1].strip())\n\n            for dep_name, constraint in info.get(\"dependencies\", {}).items():\n                if isinstance(constraint, list):\n                    for c in constraint:\n                        package.add_dependency(dep_name, c)\n\n                    continue\n\n                package.add_dependency(dep_name, constraint)\n\n            if \"source\" in info:\n                package.source_type = info[\"source\"][\"type\"]\n                package.source_url = info[\"source\"][\"url\"]\n                package.source_reference = info[\"source\"][\"reference\"]\n\n            packages.add_package(package)\n\n        return packages\n\n    def set_lock_data(self, root, packages):  # type: (...) -> bool\n        hashes = {}\n        packages = self._lock_packages(packages)\n        # Retrieving hashes\n        for package in packages:\n            if package[\"name\"] not in hashes:\n                hashes[package[\"name\"]] = []\n\n            hashes[package[\"name\"]] += package[\"hashes\"]\n            del package[\"hashes\"]\n\n        lock = document()\n        lock[\"package\"] = packages\n\n        if root.extras:\n            lock[\"extras\"] = {\n                extra: [dep.pretty_name for dep in deps]\n                for extra, deps in root.extras.items()\n            }\n\n        lock[\"metadata\"] = {\n            \"python-versions\": root.python_versions,\n            \"content-hash\": self._content_hash,\n            \"hashes\": hashes,\n        }\n\n        if not self.is_locked() or lock != self.lock_data:\n            self._write_lock_data(lock)\n\n            return True\n\n        return False\n\n    def _write_lock_data(self, data):\n        self.lock.write(data)\n\n        # Checking lock file data consistency\n        if data != self.lock.read():\n            raise RuntimeError(\"Inconsistent lock file data.\")\n\n        self._lock_data = None\n\n    def _get_content_hash(self):  # type: () -> str\n        \"\"\"\n        Returns the sha256 hash of the sorted content of the pyproject file.\n        \"\"\"\n        content = self._local_config\n\n        relevant_content = {}\n        for key in self._relevant_keys:\n            relevant_content[key] = content.get(key)\n\n        content_hash = sha256(\n            json.dumps(relevant_content, sort_keys=True).encode()\n        ).hexdigest()\n\n        return content_hash\n\n    def _get_lock_data(self):  # type: () -> dict\n        if not self._lock.exists():\n            raise RuntimeError(\"No lockfile found. Unable to read locked packages\")\n\n        return self._lock.read()\n\n    def _lock_packages(\n        self, packages\n    ):  # type: (List['poetry.packages.Package']) -> list\n        locked = []\n\n        for package in sorted(packages, key=lambda x: x.name):\n            spec = self._dump_package(package)\n\n            locked.append(spec)\n\n        return locked\n\n    def _dump_package(self, package):  # type: (poetry.packages.Package) -> dict\n        dependencies = {}\n        for dependency in sorted(package.requires, key=lambda d: d.name):\n            if dependency.is_optional() and not dependency.is_activated():\n                continue\n\n            if dependency.pretty_name not in dependencies:\n                dependencies[dependency.pretty_name] = []\n            else:\n                # Ensure we don't have mixed types in the lock file\n                for i, spec in enumerate(dependencies[dependency.pretty_name]):\n                    if not isinstance(spec, dict):\n                        dependencies[dependency.pretty_name][i] = {\"version\": spec}\n\n            constraint = {\"version\": str(dependency.pretty_constraint)}\n\n            if dependency.extras:\n                constraint[\"extras\"] = dependency.extras\n\n            if dependency.is_optional():\n                constraint[\"optional\"] = True\n\n            if not dependency.python_constraint.is_any():\n                constraint[\"python\"] = str(dependency.python_constraint)\n\n            if len(constraint) == 1:\n                dependencies[dependency.pretty_name].append(constraint[\"version\"])\n            else:\n                dependencies[dependency.pretty_name].append(constraint)\n\n        data = {\n            \"name\": package.pretty_name,\n            \"version\": package.pretty_version,\n            \"description\": package.description or \"\",\n            \"category\": package.category,\n            \"optional\": package.optional,\n            \"python-versions\": package.python_versions,\n            \"hashes\": sorted(package.hashes),\n        }\n        if not package.marker.is_any():\n            data[\"marker\"] = str(package.marker)\n\n        if package.extras:\n            extras = {}\n            for name, deps in package.extras.items():\n                extras[name] = [\n                    str(dep) if not dep.constraint.is_any() else dep.name\n                    for dep in deps\n                ]\n\n            data[\"extras\"] = extras\n\n        if dependencies:\n            for k, constraints in dependencies.items():\n                if len(constraints) == 1:\n                    dependencies[k] = constraints[0]\n\n            data[\"dependencies\"] = dependencies\n\n        if package.source_type:\n            data[\"source\"] = {\n                \"type\": package.source_type,\n                \"url\": package.source_url,\n                \"reference\": package.source_reference,\n            }\n\n        return data\n", "path": "poetry/packages/locker.py"}], "after_files": [{"content": "import json\nimport re\n\nimport poetry.packages\nimport poetry.repositories\n\nfrom hashlib import sha256\nfrom tomlkit import document\nfrom typing import List\n\nfrom poetry.utils._compat import Path\nfrom poetry.utils.toml_file import TomlFile\nfrom poetry.version.markers import parse_marker\n\n\nclass Locker(object):\n\n    _relevant_keys = [\"dependencies\", \"dev-dependencies\", \"source\", \"extras\"]\n\n    def __init__(self, lock, local_config):  # type: (Path, dict) -> None\n        self._lock = TomlFile(lock)\n        self._local_config = local_config\n        self._lock_data = None\n        self._content_hash = self._get_content_hash()\n\n    @property\n    def lock(self):  # type: () -> TomlFile\n        return self._lock\n\n    @property\n    def lock_data(self):\n        if self._lock_data is None:\n            self._lock_data = self._get_lock_data()\n\n        return self._lock_data\n\n    def is_locked(self):  # type: () -> bool\n        \"\"\"\n        Checks whether the locker has been locked (lockfile found).\n        \"\"\"\n        if not self._lock.exists():\n            return False\n\n        return \"package\" in self.lock_data\n\n    def is_fresh(self):  # type: () -> bool\n        \"\"\"\n        Checks whether the lock file is still up to date with the current hash.\n        \"\"\"\n        lock = self._lock.read()\n        metadata = lock.get(\"metadata\", {})\n\n        if \"content-hash\" in metadata:\n            return self._content_hash == lock[\"metadata\"][\"content-hash\"]\n\n        return False\n\n    def locked_repository(\n        self, with_dev_reqs=False\n    ):  # type: (bool) -> poetry.repositories.Repository\n        \"\"\"\n        Searches and returns a repository of locked packages.\n        \"\"\"\n        if not self.is_locked():\n            return poetry.repositories.Repository()\n\n        lock_data = self.lock_data\n        packages = poetry.repositories.Repository()\n\n        if with_dev_reqs:\n            locked_packages = lock_data[\"package\"]\n        else:\n            locked_packages = [\n                p for p in lock_data[\"package\"] if p[\"category\"] == \"main\"\n            ]\n\n        if not locked_packages:\n            return packages\n\n        for info in locked_packages:\n            package = poetry.packages.Package(\n                info[\"name\"], info[\"version\"], info[\"version\"]\n            )\n            package.description = info.get(\"description\", \"\")\n            package.category = info[\"category\"]\n            package.optional = info[\"optional\"]\n            package.hashes = lock_data[\"metadata\"][\"hashes\"][info[\"name\"]]\n            package.python_versions = info[\"python-versions\"]\n            extras = info.get(\"extras\", {})\n            if extras:\n                for name, deps in extras.items():\n                    package.extras[name] = []\n\n                    for dep in deps:\n                        m = re.match(r\"^(.+?)(?:\\s+\\((.+)\\))?$\", dep)\n                        dep_name = m.group(1)\n                        constraint = m.group(2) or \"*\"\n\n                        package.extras[name].append(\n                            poetry.packages.Dependency(dep_name, constraint)\n                        )\n\n            if \"marker\" in info:\n                package.marker = parse_marker(info[\"marker\"])\n            else:\n                # Compatibility for old locks\n                if \"requirements\" in info:\n                    dep = poetry.packages.Dependency(\"foo\", \"0.0.0\")\n                    for name, value in info[\"requirements\"].items():\n                        if name == \"python\":\n                            dep.python_versions = value\n                        elif name == \"platform\":\n                            dep.platform = value\n\n                    split_dep = dep.to_pep_508(False).split(\";\")\n                    if len(split_dep) > 1:\n                        package.marker = parse_marker(split_dep[1].strip())\n\n            for dep_name, constraint in info.get(\"dependencies\", {}).items():\n                if isinstance(constraint, list):\n                    for c in constraint:\n                        package.add_dependency(dep_name, c)\n\n                    continue\n\n                package.add_dependency(dep_name, constraint)\n\n            if \"source\" in info:\n                package.source_type = info[\"source\"][\"type\"]\n                package.source_url = info[\"source\"][\"url\"]\n                package.source_reference = info[\"source\"][\"reference\"]\n\n            packages.add_package(package)\n\n        return packages\n\n    def set_lock_data(self, root, packages):  # type: (...) -> bool\n        hashes = {}\n        packages = self._lock_packages(packages)\n        # Retrieving hashes\n        for package in packages:\n            if package[\"name\"] not in hashes:\n                hashes[package[\"name\"]] = []\n\n            hashes[package[\"name\"]] += package[\"hashes\"]\n            del package[\"hashes\"]\n\n        lock = document()\n        lock[\"package\"] = packages\n\n        if root.extras:\n            lock[\"extras\"] = {\n                extra: [dep.pretty_name for dep in deps]\n                for extra, deps in root.extras.items()\n            }\n\n        lock[\"metadata\"] = {\n            \"python-versions\": root.python_versions,\n            \"content-hash\": self._content_hash,\n            \"hashes\": hashes,\n        }\n\n        if not self.is_locked() or lock != self.lock_data:\n            self._write_lock_data(lock)\n\n            return True\n\n        return False\n\n    def _write_lock_data(self, data):\n        self.lock.write(data)\n\n        # Checking lock file data consistency\n        if data != self.lock.read():\n            raise RuntimeError(\"Inconsistent lock file data.\")\n\n        self._lock_data = None\n\n    def _get_content_hash(self):  # type: () -> str\n        \"\"\"\n        Returns the sha256 hash of the sorted content of the pyproject file.\n        \"\"\"\n        content = self._local_config\n\n        relevant_content = {}\n        for key in self._relevant_keys:\n            relevant_content[key] = content.get(key)\n\n        content_hash = sha256(\n            json.dumps(relevant_content, sort_keys=True).encode()\n        ).hexdigest()\n\n        return content_hash\n\n    def _get_lock_data(self):  # type: () -> dict\n        if not self._lock.exists():\n            raise RuntimeError(\"No lockfile found. Unable to read locked packages\")\n\n        return self._lock.read()\n\n    def _lock_packages(\n        self, packages\n    ):  # type: (List['poetry.packages.Package']) -> list\n        locked = []\n\n        for package in sorted(packages, key=lambda x: x.name):\n            spec = self._dump_package(package)\n\n            locked.append(spec)\n\n        return locked\n\n    def _dump_package(self, package):  # type: (poetry.packages.Package) -> dict\n        dependencies = {}\n        for dependency in sorted(package.requires, key=lambda d: d.name):\n            if dependency.is_optional() and not dependency.is_activated():\n                continue\n\n            if dependency.pretty_name not in dependencies:\n                dependencies[dependency.pretty_name] = []\n\n            constraint = {\"version\": str(dependency.pretty_constraint)}\n\n            if dependency.extras:\n                constraint[\"extras\"] = dependency.extras\n\n            if dependency.is_optional():\n                constraint[\"optional\"] = True\n\n            if not dependency.python_constraint.is_any():\n                constraint[\"python\"] = str(dependency.python_constraint)\n\n            dependencies[dependency.pretty_name].append(constraint)\n\n        # All the constraints should have the same type,\n        # but we want to simplify them if it's possible\n        for dependency, constraints in tuple(dependencies.items()):\n            if all(len(constraint) == 1 for constraint in constraints):\n                dependencies[dependency] = [\n                    constraint[\"version\"] for constraint in constraints\n                ]\n\n        data = {\n            \"name\": package.pretty_name,\n            \"version\": package.pretty_version,\n            \"description\": package.description or \"\",\n            \"category\": package.category,\n            \"optional\": package.optional,\n            \"python-versions\": package.python_versions,\n            \"hashes\": sorted(package.hashes),\n        }\n        if not package.marker.is_any():\n            data[\"marker\"] = str(package.marker)\n\n        if package.extras:\n            extras = {}\n            for name, deps in package.extras.items():\n                extras[name] = [\n                    str(dep) if not dep.constraint.is_any() else dep.name\n                    for dep in deps\n                ]\n\n            data[\"extras\"] = extras\n\n        if dependencies:\n            for k, constraints in dependencies.items():\n                if len(constraints) == 1:\n                    dependencies[k] = constraints[0]\n\n            data[\"dependencies\"] = dependencies\n\n        if package.source_type:\n            data[\"source\"] = {\n                \"type\": package.source_type,\n                \"url\": package.source_url,\n                \"reference\": package.source_reference,\n            }\n\n        return data\n", "path": "poetry/packages/locker.py"}]}