We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
API /user/{name} ambiguous
Both PATCH and POST method accept this json body:

{
  "name":  "nameOFtheuser",
  "admin": true
}

The hub will use the {name} inside the URL but if you try to call the endpoint with a PATCH method and different names (body and url) you will get 

 sqlalchemy.exc.InvalidRequestError: This Session's transaction has been rolled back due to a previous exception during flush.]

I think it would better to accept only one name and not both.

I propose to remove the name from the json body and return an error in case is provided.

https://github.com/jupyterhub/jupyterhub/blob/master/jupyterhub/apihandlers/users.py#L152
https://github.com/jupyterhub/jupyterhub/blob/master/jupyterhub/apihandlers/base.py#L141
https://github.com/jupyterhub/jupyterhub/blob/master/jupyterhub/apihandlers/base.py#L112

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `jupyterhub/apihandlers/users.py`
Content:
```
1 """User handlers"""
2 
3 # Copyright (c) Jupyter Development Team.
4 # Distributed under the terms of the Modified BSD License.
5 
6 import json
7 
8 from tornado import gen, web
9 
10 from .. import orm
11 from ..utils import admin_only
12 from .base import APIHandler
13 
14 
15 class UserListAPIHandler(APIHandler):
16     @admin_only
17     def get(self):
18         users = [ self._user_from_orm(u) for u in self.db.query(orm.User) ]
19         data = [ self.user_model(u) for u in users ]
20         self.write(json.dumps(data))
21     
22     @admin_only
23     @gen.coroutine
24     def post(self):
25         data = self.get_json_body()
26         if not data or not isinstance(data, dict) or not data.get('usernames'):
27             raise web.HTTPError(400, "Must specify at least one user to create")
28         
29         usernames = data.pop('usernames')
30         self._check_user_model(data)
31         # admin is set for all users
32         # to create admin and non-admin users requires at least two API requests
33         admin = data.get('admin', False)
34         
35         to_create = []
36         invalid_names = []
37         for name in usernames:
38             name = self.authenticator.normalize_username(name)
39             if not self.authenticator.validate_username(name):
40                 invalid_names.append(name)
41                 continue
42             user = self.find_user(name)
43             if user is not None:
44                 self.log.warning("User %s already exists" % name)
45             else:
46                 to_create.append(name)
47         
48         if invalid_names:
49             if len(invalid_names) == 1:
50                 msg = "Invalid username: %s" % invalid_names[0]
51             else:
52                 msg = "Invalid usernames: %s" % ', '.join(invalid_names)
53             raise web.HTTPError(400, msg)
54         
55         if not to_create:
56             raise web.HTTPError(400, "All %i users already exist" % len(usernames))
57         
58         created = []
59         for name in to_create:
60             user = self.user_from_username(name)
61             if admin:
62                 user.admin = True
63                 self.db.commit()
64             try:
65                 yield gen.maybe_future(self.authenticator.add_user(user))
66             except Exception as e:
67                 self.log.error("Failed to create user: %s" % name, exc_info=True)
68                 del self.users[user]
69                 raise web.HTTPError(400, "Failed to create user %s: %s" % (name, str(e)))
70             else:
71                 created.append(user)
72         
73         self.write(json.dumps([ self.user_model(u) for u in created ]))
74         self.set_status(201)
75 
76 
77 def admin_or_self(method):
78     """Decorator for restricting access to either the target user or admin"""
79     def m(self, name):
80         current = self.get_current_user()
81         if current is None:
82             raise web.HTTPError(403)
83         if not (current.name == name or current.admin):
84             raise web.HTTPError(403)
85         
86         # raise 404 if not found
87         if not self.find_user(name):
88             raise web.HTTPError(404)
89         return method(self, name)
90     return m
91 
92 class UserAPIHandler(APIHandler):
93     
94     @admin_or_self
95     def get(self, name):
96         user = self.find_user(name)
97         self.write(json.dumps(self.user_model(user)))
98     
99     @admin_only
100     @gen.coroutine
101     def post(self, name):
102         data = self.get_json_body()
103         user = self.find_user(name)
104         if user is not None:
105             raise web.HTTPError(400, "User %s already exists" % name)
106         
107         user = self.user_from_username(name)
108         if data:
109             self._check_user_model(data)
110             if 'admin' in data:
111                 user.admin = data['admin']
112                 self.db.commit()
113         
114         try:
115             yield gen.maybe_future(self.authenticator.add_user(user))
116         except Exception:
117             self.log.error("Failed to create user: %s" % name, exc_info=True)
118             # remove from registry
119             del self.users[user]
120             raise web.HTTPError(400, "Failed to create user: %s" % name)
121         
122         self.write(json.dumps(self.user_model(user)))
123         self.set_status(201)
124     
125     @admin_only
126     @gen.coroutine
127     def delete(self, name):
128         user = self.find_user(name)
129         if user is None:
130             raise web.HTTPError(404)
131         if user.name == self.get_current_user().name:
132             raise web.HTTPError(400, "Cannot delete yourself!")
133         if user.stop_pending:
134             raise web.HTTPError(400, "%s's server is in the process of stopping, please wait." % name)
135         if user.running:
136             yield self.stop_single_user(user)
137             if user.stop_pending:
138                 raise web.HTTPError(400, "%s's server is in the process of stopping, please wait." % name)
139         
140         yield gen.maybe_future(self.authenticator.delete_user(user))
141         # remove from registry
142         del self.users[user]
143         
144         self.set_status(204)
145     
146     @admin_only
147     def patch(self, name):
148         user = self.find_user(name)
149         if user is None:
150             raise web.HTTPError(404)
151         data = self.get_json_body()
152         self._check_user_model(data)
153         for key, value in data.items():
154             setattr(user, key, value)
155         self.db.commit()
156         self.write(json.dumps(self.user_model(user)))
157 
158 
159 class UserServerAPIHandler(APIHandler):
160     @gen.coroutine
161     @admin_or_self
162     def post(self, name):
163         user = self.find_user(name)
164         if user.running:
165             # include notify, so that a server that died is noticed immediately
166             state = yield user.spawner.poll_and_notify()
167             if state is None:
168                 raise web.HTTPError(400, "%s's server is already running" % name)
169 
170         options = self.get_json_body()
171         yield self.spawn_single_user(user, options=options)
172         status = 202 if user.spawn_pending else 201
173         self.set_status(status)
174 
175     @gen.coroutine
176     @admin_or_self
177     def delete(self, name):
178         user = self.find_user(name)
179         if user.stop_pending:
180             self.set_status(202)
181             return
182         if not user.running:
183             raise web.HTTPError(400, "%s's server is not running" % name)
184         # include notify, so that a server that died is noticed immediately
185         status = yield user.spawner.poll_and_notify()
186         if status is not None:
187             raise web.HTTPError(400, "%s's server is not running" % name)
188         yield self.stop_single_user(user)
189         status = 202 if user.stop_pending else 204
190         self.set_status(status)
191 
192 class UserAdminAccessAPIHandler(APIHandler):
193     """Grant admins access to single-user servers
194     
195     This handler sets the necessary cookie for an admin to login to a single-user server.
196     """
197     @admin_only
198     def post(self, name):
199         current = self.get_current_user()
200         self.log.warning("Admin user %s has requested access to %s's server",
201             current.name, name,
202         )
203         if not self.settings.get('admin_access', False):
204             raise web.HTTPError(403, "admin access to user servers disabled")
205         user = self.find_user(name)
206         if user is None:
207             raise web.HTTPError(404)
208         if not user.running:
209             raise web.HTTPError(400, "%s's server is not running" % name)
210         self.set_server_cookie(user)
211         # a service can also ask for a user cookie
212         # this code prevent to raise an error
213         if getattr(current, 'other_user_cookies', None) is not None:
214             current.other_user_cookies.add(name)
215 
216 
217 default_handlers = [
218     (r"/api/users", UserListAPIHandler),
219     (r"/api/users/([^/]+)", UserAPIHandler),
220     (r"/api/users/([^/]+)/server", UserServerAPIHandler),
221     (r"/api/users/([^/]+)/admin-access", UserAdminAccessAPIHandler),
222 ]
223
```
--- 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/jupyterhub/apihandlers/users.py b/jupyterhub/apihandlers/users.py
--- a/jupyterhub/apihandlers/users.py
+++ b/jupyterhub/apihandlers/users.py
@@ -150,6 +150,10 @@
             raise web.HTTPError(404)
         data = self.get_json_body()
         self._check_user_model(data)
+        if 'name' in data and data['name'] != name:
+            # check if the new name is already taken inside db
+            if self.find_user(data['name']):
+                raise web.HTTPError(400, "User %s already exists, username must be unique" % data['name'])
         for key, value in data.items():
             setattr(user, key, value)
         self.db.commit()
@@ -209,7 +213,8 @@
             raise web.HTTPError(400, "%s's server is not running" % name)
         self.set_server_cookie(user)
         # a service can also ask for a user cookie
-        # this code prevent to raise an error
+        # this code prevents to raise an error
+        # cause service doesn't have 'other_user_cookies'
         if getattr(current, 'other_user_cookies', None) is not None:
             current.other_user_cookies.add(name)

{"golden_diff": "diff --git a/jupyterhub/apihandlers/users.py b/jupyterhub/apihandlers/users.py\n--- a/jupyterhub/apihandlers/users.py\n+++ b/jupyterhub/apihandlers/users.py\n@@ -150,6 +150,10 @@\n             raise web.HTTPError(404)\n         data = self.get_json_body()\n         self._check_user_model(data)\n+        if 'name' in data and data['name'] != name:\n+            # check if the new name is already taken inside db\n+            if self.find_user(data['name']):\n+                raise web.HTTPError(400, \"User %s already exists, username must be unique\" % data['name'])\n         for key, value in data.items():\n             setattr(user, key, value)\n         self.db.commit()\n@@ -209,7 +213,8 @@\n             raise web.HTTPError(400, \"%s's server is not running\" % name)\n         self.set_server_cookie(user)\n         # a service can also ask for a user cookie\n-        # this code prevent to raise an error\n+        # this code prevents to raise an error\n+        # cause service doesn't have 'other_user_cookies'\n         if getattr(current, 'other_user_cookies', None) is not None:\n             current.other_user_cookies.add(name)\n", "issue": "API /user/{name} ambiguous\nBoth PATCH and POST method accept this json body:\r\n\r\n{\r\n  \"name\":  \"nameOFtheuser\",\r\n  \"admin\": true\r\n}\r\n\r\nThe hub will use the {name} inside the URL but if you try to call the endpoint with a PATCH method and different names (body and url) you will get \r\n\r\n sqlalchemy.exc.InvalidRequestError: This Session's transaction has been rolled back due to a previous exception during flush.]\r\n\r\nI think it would better to accept only one name and not both.\r\n\r\nI propose to remove the name from the json body and return an error in case is provided.\r\n\r\nhttps://github.com/jupyterhub/jupyterhub/blob/master/jupyterhub/apihandlers/users.py#L152\r\nhttps://github.com/jupyterhub/jupyterhub/blob/master/jupyterhub/apihandlers/base.py#L141\r\nhttps://github.com/jupyterhub/jupyterhub/blob/master/jupyterhub/apihandlers/base.py#L112\n", "before_files": [{"content": "\"\"\"User handlers\"\"\"\n\n# Copyright (c) Jupyter Development Team.\n# Distributed under the terms of the Modified BSD License.\n\nimport json\n\nfrom tornado import gen, web\n\nfrom .. import orm\nfrom ..utils import admin_only\nfrom .base import APIHandler\n\n\nclass UserListAPIHandler(APIHandler):\n    @admin_only\n    def get(self):\n        users = [ self._user_from_orm(u) for u in self.db.query(orm.User) ]\n        data = [ self.user_model(u) for u in users ]\n        self.write(json.dumps(data))\n    \n    @admin_only\n    @gen.coroutine\n    def post(self):\n        data = self.get_json_body()\n        if not data or not isinstance(data, dict) or not data.get('usernames'):\n            raise web.HTTPError(400, \"Must specify at least one user to create\")\n        \n        usernames = data.pop('usernames')\n        self._check_user_model(data)\n        # admin is set for all users\n        # to create admin and non-admin users requires at least two API requests\n        admin = data.get('admin', False)\n        \n        to_create = []\n        invalid_names = []\n        for name in usernames:\n            name = self.authenticator.normalize_username(name)\n            if not self.authenticator.validate_username(name):\n                invalid_names.append(name)\n                continue\n            user = self.find_user(name)\n            if user is not None:\n                self.log.warning(\"User %s already exists\" % name)\n            else:\n                to_create.append(name)\n        \n        if invalid_names:\n            if len(invalid_names) == 1:\n                msg = \"Invalid username: %s\" % invalid_names[0]\n            else:\n                msg = \"Invalid usernames: %s\" % ', '.join(invalid_names)\n            raise web.HTTPError(400, msg)\n        \n        if not to_create:\n            raise web.HTTPError(400, \"All %i users already exist\" % len(usernames))\n        \n        created = []\n        for name in to_create:\n            user = self.user_from_username(name)\n            if admin:\n                user.admin = True\n                self.db.commit()\n            try:\n                yield gen.maybe_future(self.authenticator.add_user(user))\n            except Exception as e:\n                self.log.error(\"Failed to create user: %s\" % name, exc_info=True)\n                del self.users[user]\n                raise web.HTTPError(400, \"Failed to create user %s: %s\" % (name, str(e)))\n            else:\n                created.append(user)\n        \n        self.write(json.dumps([ self.user_model(u) for u in created ]))\n        self.set_status(201)\n\n\ndef admin_or_self(method):\n    \"\"\"Decorator for restricting access to either the target user or admin\"\"\"\n    def m(self, name):\n        current = self.get_current_user()\n        if current is None:\n            raise web.HTTPError(403)\n        if not (current.name == name or current.admin):\n            raise web.HTTPError(403)\n        \n        # raise 404 if not found\n        if not self.find_user(name):\n            raise web.HTTPError(404)\n        return method(self, name)\n    return m\n\nclass UserAPIHandler(APIHandler):\n    \n    @admin_or_self\n    def get(self, name):\n        user = self.find_user(name)\n        self.write(json.dumps(self.user_model(user)))\n    \n    @admin_only\n    @gen.coroutine\n    def post(self, name):\n        data = self.get_json_body()\n        user = self.find_user(name)\n        if user is not None:\n            raise web.HTTPError(400, \"User %s already exists\" % name)\n        \n        user = self.user_from_username(name)\n        if data:\n            self._check_user_model(data)\n            if 'admin' in data:\n                user.admin = data['admin']\n                self.db.commit()\n        \n        try:\n            yield gen.maybe_future(self.authenticator.add_user(user))\n        except Exception:\n            self.log.error(\"Failed to create user: %s\" % name, exc_info=True)\n            # remove from registry\n            del self.users[user]\n            raise web.HTTPError(400, \"Failed to create user: %s\" % name)\n        \n        self.write(json.dumps(self.user_model(user)))\n        self.set_status(201)\n    \n    @admin_only\n    @gen.coroutine\n    def delete(self, name):\n        user = self.find_user(name)\n        if user is None:\n            raise web.HTTPError(404)\n        if user.name == self.get_current_user().name:\n            raise web.HTTPError(400, \"Cannot delete yourself!\")\n        if user.stop_pending:\n            raise web.HTTPError(400, \"%s's server is in the process of stopping, please wait.\" % name)\n        if user.running:\n            yield self.stop_single_user(user)\n            if user.stop_pending:\n                raise web.HTTPError(400, \"%s's server is in the process of stopping, please wait.\" % name)\n        \n        yield gen.maybe_future(self.authenticator.delete_user(user))\n        # remove from registry\n        del self.users[user]\n        \n        self.set_status(204)\n    \n    @admin_only\n    def patch(self, name):\n        user = self.find_user(name)\n        if user is None:\n            raise web.HTTPError(404)\n        data = self.get_json_body()\n        self._check_user_model(data)\n        for key, value in data.items():\n            setattr(user, key, value)\n        self.db.commit()\n        self.write(json.dumps(self.user_model(user)))\n\n\nclass UserServerAPIHandler(APIHandler):\n    @gen.coroutine\n    @admin_or_self\n    def post(self, name):\n        user = self.find_user(name)\n        if user.running:\n            # include notify, so that a server that died is noticed immediately\n            state = yield user.spawner.poll_and_notify()\n            if state is None:\n                raise web.HTTPError(400, \"%s's server is already running\" % name)\n\n        options = self.get_json_body()\n        yield self.spawn_single_user(user, options=options)\n        status = 202 if user.spawn_pending else 201\n        self.set_status(status)\n\n    @gen.coroutine\n    @admin_or_self\n    def delete(self, name):\n        user = self.find_user(name)\n        if user.stop_pending:\n            self.set_status(202)\n            return\n        if not user.running:\n            raise web.HTTPError(400, \"%s's server is not running\" % name)\n        # include notify, so that a server that died is noticed immediately\n        status = yield user.spawner.poll_and_notify()\n        if status is not None:\n            raise web.HTTPError(400, \"%s's server is not running\" % name)\n        yield self.stop_single_user(user)\n        status = 202 if user.stop_pending else 204\n        self.set_status(status)\n\nclass UserAdminAccessAPIHandler(APIHandler):\n    \"\"\"Grant admins access to single-user servers\n    \n    This handler sets the necessary cookie for an admin to login to a single-user server.\n    \"\"\"\n    @admin_only\n    def post(self, name):\n        current = self.get_current_user()\n        self.log.warning(\"Admin user %s has requested access to %s's server\",\n            current.name, name,\n        )\n        if not self.settings.get('admin_access', False):\n            raise web.HTTPError(403, \"admin access to user servers disabled\")\n        user = self.find_user(name)\n        if user is None:\n            raise web.HTTPError(404)\n        if not user.running:\n            raise web.HTTPError(400, \"%s's server is not running\" % name)\n        self.set_server_cookie(user)\n        # a service can also ask for a user cookie\n        # this code prevent to raise an error\n        if getattr(current, 'other_user_cookies', None) is not None:\n            current.other_user_cookies.add(name)\n\n\ndefault_handlers = [\n    (r\"/api/users\", UserListAPIHandler),\n    (r\"/api/users/([^/]+)\", UserAPIHandler),\n    (r\"/api/users/([^/]+)/server\", UserServerAPIHandler),\n    (r\"/api/users/([^/]+)/admin-access\", UserAdminAccessAPIHandler),\n]\n", "path": "jupyterhub/apihandlers/users.py"}], "after_files": [{"content": "\"\"\"User handlers\"\"\"\n\n# Copyright (c) Jupyter Development Team.\n# Distributed under the terms of the Modified BSD License.\n\nimport json\n\nfrom tornado import gen, web\n\nfrom .. import orm\nfrom ..utils import admin_only\nfrom .base import APIHandler\n\n\nclass UserListAPIHandler(APIHandler):\n    @admin_only\n    def get(self):\n        users = [ self._user_from_orm(u) for u in self.db.query(orm.User) ]\n        data = [ self.user_model(u) for u in users ]\n        self.write(json.dumps(data))\n    \n    @admin_only\n    @gen.coroutine\n    def post(self):\n        data = self.get_json_body()\n        if not data or not isinstance(data, dict) or not data.get('usernames'):\n            raise web.HTTPError(400, \"Must specify at least one user to create\")\n        \n        usernames = data.pop('usernames')\n        self._check_user_model(data)\n        # admin is set for all users\n        # to create admin and non-admin users requires at least two API requests\n        admin = data.get('admin', False)\n        \n        to_create = []\n        invalid_names = []\n        for name in usernames:\n            name = self.authenticator.normalize_username(name)\n            if not self.authenticator.validate_username(name):\n                invalid_names.append(name)\n                continue\n            user = self.find_user(name)\n            if user is not None:\n                self.log.warning(\"User %s already exists\" % name)\n            else:\n                to_create.append(name)\n        \n        if invalid_names:\n            if len(invalid_names) == 1:\n                msg = \"Invalid username: %s\" % invalid_names[0]\n            else:\n                msg = \"Invalid usernames: %s\" % ', '.join(invalid_names)\n            raise web.HTTPError(400, msg)\n        \n        if not to_create:\n            raise web.HTTPError(400, \"All %i users already exist\" % len(usernames))\n        \n        created = []\n        for name in to_create:\n            user = self.user_from_username(name)\n            if admin:\n                user.admin = True\n                self.db.commit()\n            try:\n                yield gen.maybe_future(self.authenticator.add_user(user))\n            except Exception as e:\n                self.log.error(\"Failed to create user: %s\" % name, exc_info=True)\n                del self.users[user]\n                raise web.HTTPError(400, \"Failed to create user %s: %s\" % (name, str(e)))\n            else:\n                created.append(user)\n        \n        self.write(json.dumps([ self.user_model(u) for u in created ]))\n        self.set_status(201)\n\n\ndef admin_or_self(method):\n    \"\"\"Decorator for restricting access to either the target user or admin\"\"\"\n    def m(self, name):\n        current = self.get_current_user()\n        if current is None:\n            raise web.HTTPError(403)\n        if not (current.name == name or current.admin):\n            raise web.HTTPError(403)\n        \n        # raise 404 if not found\n        if not self.find_user(name):\n            raise web.HTTPError(404)\n        return method(self, name)\n    return m\n\nclass UserAPIHandler(APIHandler):\n    \n    @admin_or_self\n    def get(self, name):\n        user = self.find_user(name)\n        self.write(json.dumps(self.user_model(user)))\n    \n    @admin_only\n    @gen.coroutine\n    def post(self, name):\n        data = self.get_json_body()\n        user = self.find_user(name)\n        if user is not None:\n            raise web.HTTPError(400, \"User %s already exists\" % name)\n        \n        user = self.user_from_username(name)\n        if data:\n            self._check_user_model(data)\n            if 'admin' in data:\n                user.admin = data['admin']\n                self.db.commit()\n        \n        try:\n            yield gen.maybe_future(self.authenticator.add_user(user))\n        except Exception:\n            self.log.error(\"Failed to create user: %s\" % name, exc_info=True)\n            # remove from registry\n            del self.users[user]\n            raise web.HTTPError(400, \"Failed to create user: %s\" % name)\n        \n        self.write(json.dumps(self.user_model(user)))\n        self.set_status(201)\n    \n    @admin_only\n    @gen.coroutine\n    def delete(self, name):\n        user = self.find_user(name)\n        if user is None:\n            raise web.HTTPError(404)\n        if user.name == self.get_current_user().name:\n            raise web.HTTPError(400, \"Cannot delete yourself!\")\n        if user.stop_pending:\n            raise web.HTTPError(400, \"%s's server is in the process of stopping, please wait.\" % name)\n        if user.running:\n            yield self.stop_single_user(user)\n            if user.stop_pending:\n                raise web.HTTPError(400, \"%s's server is in the process of stopping, please wait.\" % name)\n        \n        yield gen.maybe_future(self.authenticator.delete_user(user))\n        # remove from registry\n        del self.users[user]\n        \n        self.set_status(204)\n    \n    @admin_only\n    def patch(self, name):\n        user = self.find_user(name)\n        if user is None:\n            raise web.HTTPError(404)\n        data = self.get_json_body()\n        self._check_user_model(data)\n        if 'name' in data and data['name'] != name:\n            # check if the new name is already taken inside db\n            if self.find_user(data['name']):\n                raise web.HTTPError(400, \"User %s already exists, username must be unique\" % data['name'])\n        for key, value in data.items():\n            setattr(user, key, value)\n        self.db.commit()\n        self.write(json.dumps(self.user_model(user)))\n\n\nclass UserServerAPIHandler(APIHandler):\n    @gen.coroutine\n    @admin_or_self\n    def post(self, name):\n        user = self.find_user(name)\n        if user.running:\n            # include notify, so that a server that died is noticed immediately\n            state = yield user.spawner.poll_and_notify()\n            if state is None:\n                raise web.HTTPError(400, \"%s's server is already running\" % name)\n\n        options = self.get_json_body()\n        yield self.spawn_single_user(user, options=options)\n        status = 202 if user.spawn_pending else 201\n        self.set_status(status)\n\n    @gen.coroutine\n    @admin_or_self\n    def delete(self, name):\n        user = self.find_user(name)\n        if user.stop_pending:\n            self.set_status(202)\n            return\n        if not user.running:\n            raise web.HTTPError(400, \"%s's server is not running\" % name)\n        # include notify, so that a server that died is noticed immediately\n        status = yield user.spawner.poll_and_notify()\n        if status is not None:\n            raise web.HTTPError(400, \"%s's server is not running\" % name)\n        yield self.stop_single_user(user)\n        status = 202 if user.stop_pending else 204\n        self.set_status(status)\n\nclass UserAdminAccessAPIHandler(APIHandler):\n    \"\"\"Grant admins access to single-user servers\n    \n    This handler sets the necessary cookie for an admin to login to a single-user server.\n    \"\"\"\n    @admin_only\n    def post(self, name):\n        current = self.get_current_user()\n        self.log.warning(\"Admin user %s has requested access to %s's server\",\n            current.name, name,\n        )\n        if not self.settings.get('admin_access', False):\n            raise web.HTTPError(403, \"admin access to user servers disabled\")\n        user = self.find_user(name)\n        if user is None:\n            raise web.HTTPError(404)\n        if not user.running:\n            raise web.HTTPError(400, \"%s's server is not running\" % name)\n        self.set_server_cookie(user)\n        # a service can also ask for a user cookie\n        # this code prevents to raise an error\n        # cause service doesn't have 'other_user_cookies'\n        if getattr(current, 'other_user_cookies', None) is not None:\n            current.other_user_cookies.add(name)\n\n\ndefault_handlers = [\n    (r\"/api/users\", UserListAPIHandler),\n    (r\"/api/users/([^/]+)\", UserAPIHandler),\n    (r\"/api/users/([^/]+)/server\", UserServerAPIHandler),\n    (r\"/api/users/([^/]+)/admin-access\", UserAdminAccessAPIHandler),\n]\n", "path": "jupyterhub/apihandlers/users.py"}]}