We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Should the Blog example use bcrypt's checkpw instead of ==
bcrypt 3.1.0 added `checkpw` which (I believe) is the recommended way to compare passwords to the hashed password now. 
- `checkpw` code [here](https://github.com/pyca/bcrypt/blob/master/src/bcrypt/__init__.py#L97)
- tornado's blog example using `==` [here](https://github.com/tornadoweb/tornado/blob/stable/demos/blog/blog.py#L211)

The current example looks like this:

``` python
    @gen.coroutine
    def post(self):
        # [...]
        hashed_password = yield executor.submit(
            bcrypt.hashpw, 
            tornado.escape.utf8(self.get_argument("password")),
            tornado.escape.utf8(author.hashed_password))
        if hashed_password == author.hashed_password:
            self.set_secure_cookie("blogdemo_user", str(author.id))
            self.redirect(self.get_argument("next", "/"))
        else:
            self.render("login.html", error="incorrect password")
```

But maybe it should look like this?

``` python
    @gen.coroutine
    def post(self):
        # [...]
        password_equal = yield executor.submit(
            bcrypt.checkpw, 
            tornado.escape.utf8(self.get_argument("password")),
            tornado.escape.utf8(author.hashed_password))
        if password_equal:
            self.set_secure_cookie("blogdemo_user", str(author.id))
            self.redirect(self.get_argument("next", "/"))
        else:
            self.render("login.html", error="incorrect password")
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `demos/blog/blog.py`
Content:
```
1 #!/usr/bin/env python3
2 #
3 # Copyright 2009 Facebook
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License"); you may
6 # not use this file except in compliance with the License. You may obtain
7 # a copy of the License at
8 #
9 #     http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 # License for the specific language governing permissions and limitations
15 # under the License.
16 
17 import aiopg
18 import bcrypt
19 import markdown
20 import os.path
21 import psycopg2
22 import re
23 import tornado.escape
24 import tornado.httpserver
25 import tornado.ioloop
26 import tornado.locks
27 import tornado.options
28 import tornado.web
29 import unicodedata
30 
31 from tornado.options import define, options
32 
33 define("port", default=8888, help="run on the given port", type=int)
34 define("db_host", default="127.0.0.1", help="blog database host")
35 define("db_port", default=5432, help="blog database port")
36 define("db_database", default="blog", help="blog database name")
37 define("db_user", default="blog", help="blog database user")
38 define("db_password", default="blog", help="blog database password")
39 
40 
41 class NoResultError(Exception):
42     pass
43 
44 
45 async def maybe_create_tables(db):
46     try:
47         with (await db.cursor()) as cur:
48             await cur.execute("SELECT COUNT(*) FROM entries LIMIT 1")
49             await cur.fetchone()
50     except psycopg2.ProgrammingError:
51         with open("schema.sql") as f:
52             schema = f.read()
53         with (await db.cursor()) as cur:
54             await cur.execute(schema)
55 
56 
57 class Application(tornado.web.Application):
58     def __init__(self, db):
59         self.db = db
60         handlers = [
61             (r"/", HomeHandler),
62             (r"/archive", ArchiveHandler),
63             (r"/feed", FeedHandler),
64             (r"/entry/([^/]+)", EntryHandler),
65             (r"/compose", ComposeHandler),
66             (r"/auth/create", AuthCreateHandler),
67             (r"/auth/login", AuthLoginHandler),
68             (r"/auth/logout", AuthLogoutHandler),
69         ]
70         settings = dict(
71             blog_title=u"Tornado Blog",
72             template_path=os.path.join(os.path.dirname(__file__), "templates"),
73             static_path=os.path.join(os.path.dirname(__file__), "static"),
74             ui_modules={"Entry": EntryModule},
75             xsrf_cookies=True,
76             cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
77             login_url="/auth/login",
78             debug=True,
79         )
80         super(Application, self).__init__(handlers, **settings)
81 
82 
83 class BaseHandler(tornado.web.RequestHandler):
84     def row_to_obj(self, row, cur):
85         """Convert a SQL row to an object supporting dict and attribute access."""
86         obj = tornado.util.ObjectDict()
87         for val, desc in zip(row, cur.description):
88             obj[desc.name] = val
89         return obj
90 
91     async def execute(self, stmt, *args):
92         """Execute a SQL statement.
93 
94         Must be called with ``await self.execute(...)``
95         """
96         with (await self.application.db.cursor()) as cur:
97             await cur.execute(stmt, args)
98 
99     async def query(self, stmt, *args):
100         """Query for a list of results.
101 
102         Typical usage::
103 
104             results = await self.query(...)
105 
106         Or::
107 
108             for row in await self.query(...)
109         """
110         with (await self.application.db.cursor()) as cur:
111             await cur.execute(stmt, args)
112             return [self.row_to_obj(row, cur) for row in await cur.fetchall()]
113 
114     async def queryone(self, stmt, *args):
115         """Query for exactly one result.
116 
117         Raises NoResultError if there are no results, or ValueError if
118         there are more than one.
119         """
120         results = await self.query(stmt, *args)
121         if len(results) == 0:
122             raise NoResultError()
123         elif len(results) > 1:
124             raise ValueError("Expected 1 result, got %d" % len(results))
125         return results[0]
126 
127     async def prepare(self):
128         # get_current_user cannot be a coroutine, so set
129         # self.current_user in prepare instead.
130         user_id = self.get_secure_cookie("blogdemo_user")
131         if user_id:
132             self.current_user = await self.queryone(
133                 "SELECT * FROM authors WHERE id = %s", int(user_id)
134             )
135 
136     async def any_author_exists(self):
137         return bool(await self.query("SELECT * FROM authors LIMIT 1"))
138 
139 
140 class HomeHandler(BaseHandler):
141     async def get(self):
142         entries = await self.query(
143             "SELECT * FROM entries ORDER BY published DESC LIMIT 5"
144         )
145         if not entries:
146             self.redirect("/compose")
147             return
148         self.render("home.html", entries=entries)
149 
150 
151 class EntryHandler(BaseHandler):
152     async def get(self, slug):
153         entry = await self.queryone("SELECT * FROM entries WHERE slug = %s", slug)
154         if not entry:
155             raise tornado.web.HTTPError(404)
156         self.render("entry.html", entry=entry)
157 
158 
159 class ArchiveHandler(BaseHandler):
160     async def get(self):
161         entries = await self.query("SELECT * FROM entries ORDER BY published DESC")
162         self.render("archive.html", entries=entries)
163 
164 
165 class FeedHandler(BaseHandler):
166     async def get(self):
167         entries = await self.query(
168             "SELECT * FROM entries ORDER BY published DESC LIMIT 10"
169         )
170         self.set_header("Content-Type", "application/atom+xml")
171         self.render("feed.xml", entries=entries)
172 
173 
174 class ComposeHandler(BaseHandler):
175     @tornado.web.authenticated
176     async def get(self):
177         id = self.get_argument("id", None)
178         entry = None
179         if id:
180             entry = await self.queryone("SELECT * FROM entries WHERE id = %s", int(id))
181         self.render("compose.html", entry=entry)
182 
183     @tornado.web.authenticated
184     async def post(self):
185         id = self.get_argument("id", None)
186         title = self.get_argument("title")
187         text = self.get_argument("markdown")
188         html = markdown.markdown(text)
189         if id:
190             try:
191                 entry = await self.queryone(
192                     "SELECT * FROM entries WHERE id = %s", int(id)
193                 )
194             except NoResultError:
195                 raise tornado.web.HTTPError(404)
196             slug = entry.slug
197             await self.execute(
198                 "UPDATE entries SET title = %s, markdown = %s, html = %s "
199                 "WHERE id = %s",
200                 title,
201                 text,
202                 html,
203                 int(id),
204             )
205         else:
206             slug = unicodedata.normalize("NFKD", title)
207             slug = re.sub(r"[^\w]+", " ", slug)
208             slug = "-".join(slug.lower().strip().split())
209             slug = slug.encode("ascii", "ignore").decode("ascii")
210             if not slug:
211                 slug = "entry"
212             while True:
213                 e = await self.query("SELECT * FROM entries WHERE slug = %s", slug)
214                 if not e:
215                     break
216                 slug += "-2"
217             await self.execute(
218                 "INSERT INTO entries (author_id,title,slug,markdown,html,published,updated)"
219                 "VALUES (%s,%s,%s,%s,%s,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)",
220                 self.current_user.id,
221                 title,
222                 slug,
223                 text,
224                 html,
225             )
226         self.redirect("/entry/" + slug)
227 
228 
229 class AuthCreateHandler(BaseHandler):
230     def get(self):
231         self.render("create_author.html")
232 
233     async def post(self):
234         if await self.any_author_exists():
235             raise tornado.web.HTTPError(400, "author already created")
236         hashed_password = await tornado.ioloop.IOLoop.current().run_in_executor(
237             None,
238             bcrypt.hashpw,
239             tornado.escape.utf8(self.get_argument("password")),
240             bcrypt.gensalt(),
241         )
242         author = await self.queryone(
243             "INSERT INTO authors (email, name, hashed_password) "
244             "VALUES (%s, %s, %s) RETURNING id",
245             self.get_argument("email"),
246             self.get_argument("name"),
247             tornado.escape.to_unicode(hashed_password),
248         )
249         self.set_secure_cookie("blogdemo_user", str(author.id))
250         self.redirect(self.get_argument("next", "/"))
251 
252 
253 class AuthLoginHandler(BaseHandler):
254     async def get(self):
255         # If there are no authors, redirect to the account creation page.
256         if not await self.any_author_exists():
257             self.redirect("/auth/create")
258         else:
259             self.render("login.html", error=None)
260 
261     async def post(self):
262         try:
263             author = await self.queryone(
264                 "SELECT * FROM authors WHERE email = %s", self.get_argument("email")
265             )
266         except NoResultError:
267             self.render("login.html", error="email not found")
268             return
269         hashed_password = await tornado.ioloop.IOLoop.current().run_in_executor(
270             None,
271             bcrypt.hashpw,
272             tornado.escape.utf8(self.get_argument("password")),
273             tornado.escape.utf8(author.hashed_password),
274         )
275         hashed_password = tornado.escape.to_unicode(hashed_password)
276         if hashed_password == author.hashed_password:
277             self.set_secure_cookie("blogdemo_user", str(author.id))
278             self.redirect(self.get_argument("next", "/"))
279         else:
280             self.render("login.html", error="incorrect password")
281 
282 
283 class AuthLogoutHandler(BaseHandler):
284     def get(self):
285         self.clear_cookie("blogdemo_user")
286         self.redirect(self.get_argument("next", "/"))
287 
288 
289 class EntryModule(tornado.web.UIModule):
290     def render(self, entry):
291         return self.render_string("modules/entry.html", entry=entry)
292 
293 
294 async def main():
295     tornado.options.parse_command_line()
296 
297     # Create the global connection pool.
298     async with aiopg.create_pool(
299         host=options.db_host,
300         port=options.db_port,
301         user=options.db_user,
302         password=options.db_password,
303         dbname=options.db_database,
304     ) as db:
305         await maybe_create_tables(db)
306         app = Application(db)
307         app.listen(options.port)
308 
309         # In this demo the server will simply run until interrupted
310         # with Ctrl-C, but if you want to shut down more gracefully,
311         # call shutdown_event.set().
312         shutdown_event = tornado.locks.Event()
313         await shutdown_event.wait()
314 
315 
316 if __name__ == "__main__":
317     tornado.ioloop.IOLoop.current().run_sync(main)
318
```
--- 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/demos/blog/blog.py b/demos/blog/blog.py
--- a/demos/blog/blog.py
+++ b/demos/blog/blog.py
@@ -266,14 +266,13 @@
         except NoResultError:
             self.render("login.html", error="email not found")
             return
-        hashed_password = await tornado.ioloop.IOLoop.current().run_in_executor(
+        password_equal = await tornado.ioloop.IOLoop.current().run_in_executor(
             None,
-            bcrypt.hashpw,
+            bcrypt.checkpw,
             tornado.escape.utf8(self.get_argument("password")),
             tornado.escape.utf8(author.hashed_password),
         )
-        hashed_password = tornado.escape.to_unicode(hashed_password)
-        if hashed_password == author.hashed_password:
+        if password_equal:
             self.set_secure_cookie("blogdemo_user", str(author.id))
             self.redirect(self.get_argument("next", "/"))
         else:

{"golden_diff": "diff --git a/demos/blog/blog.py b/demos/blog/blog.py\n--- a/demos/blog/blog.py\n+++ b/demos/blog/blog.py\n@@ -266,14 +266,13 @@\n         except NoResultError:\n             self.render(\"login.html\", error=\"email not found\")\n             return\n-        hashed_password = await tornado.ioloop.IOLoop.current().run_in_executor(\n+        password_equal = await tornado.ioloop.IOLoop.current().run_in_executor(\n             None,\n-            bcrypt.hashpw,\n+            bcrypt.checkpw,\n             tornado.escape.utf8(self.get_argument(\"password\")),\n             tornado.escape.utf8(author.hashed_password),\n         )\n-        hashed_password = tornado.escape.to_unicode(hashed_password)\n-        if hashed_password == author.hashed_password:\n+        if password_equal:\n             self.set_secure_cookie(\"blogdemo_user\", str(author.id))\n             self.redirect(self.get_argument(\"next\", \"/\"))\n         else:\n", "issue": "Should the Blog example use bcrypt's checkpw instead of ==\nbcrypt 3.1.0 added `checkpw` which (I believe) is the recommended way to compare passwords to the hashed password now. \n- `checkpw` code [here](https://github.com/pyca/bcrypt/blob/master/src/bcrypt/__init__.py#L97)\n- tornado's blog example using `==` [here](https://github.com/tornadoweb/tornado/blob/stable/demos/blog/blog.py#L211)\n\nThe current example looks like this:\n\n``` python\n    @gen.coroutine\n    def post(self):\n        # [...]\n        hashed_password = yield executor.submit(\n            bcrypt.hashpw, \n            tornado.escape.utf8(self.get_argument(\"password\")),\n            tornado.escape.utf8(author.hashed_password))\n        if hashed_password == author.hashed_password:\n            self.set_secure_cookie(\"blogdemo_user\", str(author.id))\n            self.redirect(self.get_argument(\"next\", \"/\"))\n        else:\n            self.render(\"login.html\", error=\"incorrect password\")\n```\n\nBut maybe it should look like this?\n\n``` python\n    @gen.coroutine\n    def post(self):\n        # [...]\n        password_equal = yield executor.submit(\n            bcrypt.checkpw, \n            tornado.escape.utf8(self.get_argument(\"password\")),\n            tornado.escape.utf8(author.hashed_password))\n        if password_equal:\n            self.set_secure_cookie(\"blogdemo_user\", str(author.id))\n            self.redirect(self.get_argument(\"next\", \"/\"))\n        else:\n            self.render(\"login.html\", error=\"incorrect password\")\n```\n\n", "before_files": [{"content": "#!/usr/bin/env python3\n#\n# Copyright 2009 Facebook\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\n\nimport aiopg\nimport bcrypt\nimport markdown\nimport os.path\nimport psycopg2\nimport re\nimport tornado.escape\nimport tornado.httpserver\nimport tornado.ioloop\nimport tornado.locks\nimport tornado.options\nimport tornado.web\nimport unicodedata\n\nfrom tornado.options import define, options\n\ndefine(\"port\", default=8888, help=\"run on the given port\", type=int)\ndefine(\"db_host\", default=\"127.0.0.1\", help=\"blog database host\")\ndefine(\"db_port\", default=5432, help=\"blog database port\")\ndefine(\"db_database\", default=\"blog\", help=\"blog database name\")\ndefine(\"db_user\", default=\"blog\", help=\"blog database user\")\ndefine(\"db_password\", default=\"blog\", help=\"blog database password\")\n\n\nclass NoResultError(Exception):\n    pass\n\n\nasync def maybe_create_tables(db):\n    try:\n        with (await db.cursor()) as cur:\n            await cur.execute(\"SELECT COUNT(*) FROM entries LIMIT 1\")\n            await cur.fetchone()\n    except psycopg2.ProgrammingError:\n        with open(\"schema.sql\") as f:\n            schema = f.read()\n        with (await db.cursor()) as cur:\n            await cur.execute(schema)\n\n\nclass Application(tornado.web.Application):\n    def __init__(self, db):\n        self.db = db\n        handlers = [\n            (r\"/\", HomeHandler),\n            (r\"/archive\", ArchiveHandler),\n            (r\"/feed\", FeedHandler),\n            (r\"/entry/([^/]+)\", EntryHandler),\n            (r\"/compose\", ComposeHandler),\n            (r\"/auth/create\", AuthCreateHandler),\n            (r\"/auth/login\", AuthLoginHandler),\n            (r\"/auth/logout\", AuthLogoutHandler),\n        ]\n        settings = dict(\n            blog_title=u\"Tornado Blog\",\n            template_path=os.path.join(os.path.dirname(__file__), \"templates\"),\n            static_path=os.path.join(os.path.dirname(__file__), \"static\"),\n            ui_modules={\"Entry\": EntryModule},\n            xsrf_cookies=True,\n            cookie_secret=\"__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__\",\n            login_url=\"/auth/login\",\n            debug=True,\n        )\n        super(Application, self).__init__(handlers, **settings)\n\n\nclass BaseHandler(tornado.web.RequestHandler):\n    def row_to_obj(self, row, cur):\n        \"\"\"Convert a SQL row to an object supporting dict and attribute access.\"\"\"\n        obj = tornado.util.ObjectDict()\n        for val, desc in zip(row, cur.description):\n            obj[desc.name] = val\n        return obj\n\n    async def execute(self, stmt, *args):\n        \"\"\"Execute a SQL statement.\n\n        Must be called with ``await self.execute(...)``\n        \"\"\"\n        with (await self.application.db.cursor()) as cur:\n            await cur.execute(stmt, args)\n\n    async def query(self, stmt, *args):\n        \"\"\"Query for a list of results.\n\n        Typical usage::\n\n            results = await self.query(...)\n\n        Or::\n\n            for row in await self.query(...)\n        \"\"\"\n        with (await self.application.db.cursor()) as cur:\n            await cur.execute(stmt, args)\n            return [self.row_to_obj(row, cur) for row in await cur.fetchall()]\n\n    async def queryone(self, stmt, *args):\n        \"\"\"Query for exactly one result.\n\n        Raises NoResultError if there are no results, or ValueError if\n        there are more than one.\n        \"\"\"\n        results = await self.query(stmt, *args)\n        if len(results) == 0:\n            raise NoResultError()\n        elif len(results) > 1:\n            raise ValueError(\"Expected 1 result, got %d\" % len(results))\n        return results[0]\n\n    async def prepare(self):\n        # get_current_user cannot be a coroutine, so set\n        # self.current_user in prepare instead.\n        user_id = self.get_secure_cookie(\"blogdemo_user\")\n        if user_id:\n            self.current_user = await self.queryone(\n                \"SELECT * FROM authors WHERE id = %s\", int(user_id)\n            )\n\n    async def any_author_exists(self):\n        return bool(await self.query(\"SELECT * FROM authors LIMIT 1\"))\n\n\nclass HomeHandler(BaseHandler):\n    async def get(self):\n        entries = await self.query(\n            \"SELECT * FROM entries ORDER BY published DESC LIMIT 5\"\n        )\n        if not entries:\n            self.redirect(\"/compose\")\n            return\n        self.render(\"home.html\", entries=entries)\n\n\nclass EntryHandler(BaseHandler):\n    async def get(self, slug):\n        entry = await self.queryone(\"SELECT * FROM entries WHERE slug = %s\", slug)\n        if not entry:\n            raise tornado.web.HTTPError(404)\n        self.render(\"entry.html\", entry=entry)\n\n\nclass ArchiveHandler(BaseHandler):\n    async def get(self):\n        entries = await self.query(\"SELECT * FROM entries ORDER BY published DESC\")\n        self.render(\"archive.html\", entries=entries)\n\n\nclass FeedHandler(BaseHandler):\n    async def get(self):\n        entries = await self.query(\n            \"SELECT * FROM entries ORDER BY published DESC LIMIT 10\"\n        )\n        self.set_header(\"Content-Type\", \"application/atom+xml\")\n        self.render(\"feed.xml\", entries=entries)\n\n\nclass ComposeHandler(BaseHandler):\n    @tornado.web.authenticated\n    async def get(self):\n        id = self.get_argument(\"id\", None)\n        entry = None\n        if id:\n            entry = await self.queryone(\"SELECT * FROM entries WHERE id = %s\", int(id))\n        self.render(\"compose.html\", entry=entry)\n\n    @tornado.web.authenticated\n    async def post(self):\n        id = self.get_argument(\"id\", None)\n        title = self.get_argument(\"title\")\n        text = self.get_argument(\"markdown\")\n        html = markdown.markdown(text)\n        if id:\n            try:\n                entry = await self.queryone(\n                    \"SELECT * FROM entries WHERE id = %s\", int(id)\n                )\n            except NoResultError:\n                raise tornado.web.HTTPError(404)\n            slug = entry.slug\n            await self.execute(\n                \"UPDATE entries SET title = %s, markdown = %s, html = %s \"\n                \"WHERE id = %s\",\n                title,\n                text,\n                html,\n                int(id),\n            )\n        else:\n            slug = unicodedata.normalize(\"NFKD\", title)\n            slug = re.sub(r\"[^\\w]+\", \" \", slug)\n            slug = \"-\".join(slug.lower().strip().split())\n            slug = slug.encode(\"ascii\", \"ignore\").decode(\"ascii\")\n            if not slug:\n                slug = \"entry\"\n            while True:\n                e = await self.query(\"SELECT * FROM entries WHERE slug = %s\", slug)\n                if not e:\n                    break\n                slug += \"-2\"\n            await self.execute(\n                \"INSERT INTO entries (author_id,title,slug,markdown,html,published,updated)\"\n                \"VALUES (%s,%s,%s,%s,%s,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)\",\n                self.current_user.id,\n                title,\n                slug,\n                text,\n                html,\n            )\n        self.redirect(\"/entry/\" + slug)\n\n\nclass AuthCreateHandler(BaseHandler):\n    def get(self):\n        self.render(\"create_author.html\")\n\n    async def post(self):\n        if await self.any_author_exists():\n            raise tornado.web.HTTPError(400, \"author already created\")\n        hashed_password = await tornado.ioloop.IOLoop.current().run_in_executor(\n            None,\n            bcrypt.hashpw,\n            tornado.escape.utf8(self.get_argument(\"password\")),\n            bcrypt.gensalt(),\n        )\n        author = await self.queryone(\n            \"INSERT INTO authors (email, name, hashed_password) \"\n            \"VALUES (%s, %s, %s) RETURNING id\",\n            self.get_argument(\"email\"),\n            self.get_argument(\"name\"),\n            tornado.escape.to_unicode(hashed_password),\n        )\n        self.set_secure_cookie(\"blogdemo_user\", str(author.id))\n        self.redirect(self.get_argument(\"next\", \"/\"))\n\n\nclass AuthLoginHandler(BaseHandler):\n    async def get(self):\n        # If there are no authors, redirect to the account creation page.\n        if not await self.any_author_exists():\n            self.redirect(\"/auth/create\")\n        else:\n            self.render(\"login.html\", error=None)\n\n    async def post(self):\n        try:\n            author = await self.queryone(\n                \"SELECT * FROM authors WHERE email = %s\", self.get_argument(\"email\")\n            )\n        except NoResultError:\n            self.render(\"login.html\", error=\"email not found\")\n            return\n        hashed_password = await tornado.ioloop.IOLoop.current().run_in_executor(\n            None,\n            bcrypt.hashpw,\n            tornado.escape.utf8(self.get_argument(\"password\")),\n            tornado.escape.utf8(author.hashed_password),\n        )\n        hashed_password = tornado.escape.to_unicode(hashed_password)\n        if hashed_password == author.hashed_password:\n            self.set_secure_cookie(\"blogdemo_user\", str(author.id))\n            self.redirect(self.get_argument(\"next\", \"/\"))\n        else:\n            self.render(\"login.html\", error=\"incorrect password\")\n\n\nclass AuthLogoutHandler(BaseHandler):\n    def get(self):\n        self.clear_cookie(\"blogdemo_user\")\n        self.redirect(self.get_argument(\"next\", \"/\"))\n\n\nclass EntryModule(tornado.web.UIModule):\n    def render(self, entry):\n        return self.render_string(\"modules/entry.html\", entry=entry)\n\n\nasync def main():\n    tornado.options.parse_command_line()\n\n    # Create the global connection pool.\n    async with aiopg.create_pool(\n        host=options.db_host,\n        port=options.db_port,\n        user=options.db_user,\n        password=options.db_password,\n        dbname=options.db_database,\n    ) as db:\n        await maybe_create_tables(db)\n        app = Application(db)\n        app.listen(options.port)\n\n        # In this demo the server will simply run until interrupted\n        # with Ctrl-C, but if you want to shut down more gracefully,\n        # call shutdown_event.set().\n        shutdown_event = tornado.locks.Event()\n        await shutdown_event.wait()\n\n\nif __name__ == \"__main__\":\n    tornado.ioloop.IOLoop.current().run_sync(main)\n", "path": "demos/blog/blog.py"}], "after_files": [{"content": "#!/usr/bin/env python3\n#\n# Copyright 2009 Facebook\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\n\nimport aiopg\nimport bcrypt\nimport markdown\nimport os.path\nimport psycopg2\nimport re\nimport tornado.escape\nimport tornado.httpserver\nimport tornado.ioloop\nimport tornado.locks\nimport tornado.options\nimport tornado.web\nimport unicodedata\n\nfrom tornado.options import define, options\n\ndefine(\"port\", default=8888, help=\"run on the given port\", type=int)\ndefine(\"db_host\", default=\"127.0.0.1\", help=\"blog database host\")\ndefine(\"db_port\", default=5432, help=\"blog database port\")\ndefine(\"db_database\", default=\"blog\", help=\"blog database name\")\ndefine(\"db_user\", default=\"blog\", help=\"blog database user\")\ndefine(\"db_password\", default=\"blog\", help=\"blog database password\")\n\n\nclass NoResultError(Exception):\n    pass\n\n\nasync def maybe_create_tables(db):\n    try:\n        with (await db.cursor()) as cur:\n            await cur.execute(\"SELECT COUNT(*) FROM entries LIMIT 1\")\n            await cur.fetchone()\n    except psycopg2.ProgrammingError:\n        with open(\"schema.sql\") as f:\n            schema = f.read()\n        with (await db.cursor()) as cur:\n            await cur.execute(schema)\n\n\nclass Application(tornado.web.Application):\n    def __init__(self, db):\n        self.db = db\n        handlers = [\n            (r\"/\", HomeHandler),\n            (r\"/archive\", ArchiveHandler),\n            (r\"/feed\", FeedHandler),\n            (r\"/entry/([^/]+)\", EntryHandler),\n            (r\"/compose\", ComposeHandler),\n            (r\"/auth/create\", AuthCreateHandler),\n            (r\"/auth/login\", AuthLoginHandler),\n            (r\"/auth/logout\", AuthLogoutHandler),\n        ]\n        settings = dict(\n            blog_title=u\"Tornado Blog\",\n            template_path=os.path.join(os.path.dirname(__file__), \"templates\"),\n            static_path=os.path.join(os.path.dirname(__file__), \"static\"),\n            ui_modules={\"Entry\": EntryModule},\n            xsrf_cookies=True,\n            cookie_secret=\"__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__\",\n            login_url=\"/auth/login\",\n            debug=True,\n        )\n        super(Application, self).__init__(handlers, **settings)\n\n\nclass BaseHandler(tornado.web.RequestHandler):\n    def row_to_obj(self, row, cur):\n        \"\"\"Convert a SQL row to an object supporting dict and attribute access.\"\"\"\n        obj = tornado.util.ObjectDict()\n        for val, desc in zip(row, cur.description):\n            obj[desc.name] = val\n        return obj\n\n    async def execute(self, stmt, *args):\n        \"\"\"Execute a SQL statement.\n\n        Must be called with ``await self.execute(...)``\n        \"\"\"\n        with (await self.application.db.cursor()) as cur:\n            await cur.execute(stmt, args)\n\n    async def query(self, stmt, *args):\n        \"\"\"Query for a list of results.\n\n        Typical usage::\n\n            results = await self.query(...)\n\n        Or::\n\n            for row in await self.query(...)\n        \"\"\"\n        with (await self.application.db.cursor()) as cur:\n            await cur.execute(stmt, args)\n            return [self.row_to_obj(row, cur) for row in await cur.fetchall()]\n\n    async def queryone(self, stmt, *args):\n        \"\"\"Query for exactly one result.\n\n        Raises NoResultError if there are no results, or ValueError if\n        there are more than one.\n        \"\"\"\n        results = await self.query(stmt, *args)\n        if len(results) == 0:\n            raise NoResultError()\n        elif len(results) > 1:\n            raise ValueError(\"Expected 1 result, got %d\" % len(results))\n        return results[0]\n\n    async def prepare(self):\n        # get_current_user cannot be a coroutine, so set\n        # self.current_user in prepare instead.\n        user_id = self.get_secure_cookie(\"blogdemo_user\")\n        if user_id:\n            self.current_user = await self.queryone(\n                \"SELECT * FROM authors WHERE id = %s\", int(user_id)\n            )\n\n    async def any_author_exists(self):\n        return bool(await self.query(\"SELECT * FROM authors LIMIT 1\"))\n\n\nclass HomeHandler(BaseHandler):\n    async def get(self):\n        entries = await self.query(\n            \"SELECT * FROM entries ORDER BY published DESC LIMIT 5\"\n        )\n        if not entries:\n            self.redirect(\"/compose\")\n            return\n        self.render(\"home.html\", entries=entries)\n\n\nclass EntryHandler(BaseHandler):\n    async def get(self, slug):\n        entry = await self.queryone(\"SELECT * FROM entries WHERE slug = %s\", slug)\n        if not entry:\n            raise tornado.web.HTTPError(404)\n        self.render(\"entry.html\", entry=entry)\n\n\nclass ArchiveHandler(BaseHandler):\n    async def get(self):\n        entries = await self.query(\"SELECT * FROM entries ORDER BY published DESC\")\n        self.render(\"archive.html\", entries=entries)\n\n\nclass FeedHandler(BaseHandler):\n    async def get(self):\n        entries = await self.query(\n            \"SELECT * FROM entries ORDER BY published DESC LIMIT 10\"\n        )\n        self.set_header(\"Content-Type\", \"application/atom+xml\")\n        self.render(\"feed.xml\", entries=entries)\n\n\nclass ComposeHandler(BaseHandler):\n    @tornado.web.authenticated\n    async def get(self):\n        id = self.get_argument(\"id\", None)\n        entry = None\n        if id:\n            entry = await self.queryone(\"SELECT * FROM entries WHERE id = %s\", int(id))\n        self.render(\"compose.html\", entry=entry)\n\n    @tornado.web.authenticated\n    async def post(self):\n        id = self.get_argument(\"id\", None)\n        title = self.get_argument(\"title\")\n        text = self.get_argument(\"markdown\")\n        html = markdown.markdown(text)\n        if id:\n            try:\n                entry = await self.queryone(\n                    \"SELECT * FROM entries WHERE id = %s\", int(id)\n                )\n            except NoResultError:\n                raise tornado.web.HTTPError(404)\n            slug = entry.slug\n            await self.execute(\n                \"UPDATE entries SET title = %s, markdown = %s, html = %s \"\n                \"WHERE id = %s\",\n                title,\n                text,\n                html,\n                int(id),\n            )\n        else:\n            slug = unicodedata.normalize(\"NFKD\", title)\n            slug = re.sub(r\"[^\\w]+\", \" \", slug)\n            slug = \"-\".join(slug.lower().strip().split())\n            slug = slug.encode(\"ascii\", \"ignore\").decode(\"ascii\")\n            if not slug:\n                slug = \"entry\"\n            while True:\n                e = await self.query(\"SELECT * FROM entries WHERE slug = %s\", slug)\n                if not e:\n                    break\n                slug += \"-2\"\n            await self.execute(\n                \"INSERT INTO entries (author_id,title,slug,markdown,html,published,updated)\"\n                \"VALUES (%s,%s,%s,%s,%s,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)\",\n                self.current_user.id,\n                title,\n                slug,\n                text,\n                html,\n            )\n        self.redirect(\"/entry/\" + slug)\n\n\nclass AuthCreateHandler(BaseHandler):\n    def get(self):\n        self.render(\"create_author.html\")\n\n    async def post(self):\n        if await self.any_author_exists():\n            raise tornado.web.HTTPError(400, \"author already created\")\n        hashed_password = await tornado.ioloop.IOLoop.current().run_in_executor(\n            None,\n            bcrypt.hashpw,\n            tornado.escape.utf8(self.get_argument(\"password\")),\n            bcrypt.gensalt(),\n        )\n        author = await self.queryone(\n            \"INSERT INTO authors (email, name, hashed_password) \"\n            \"VALUES (%s, %s, %s) RETURNING id\",\n            self.get_argument(\"email\"),\n            self.get_argument(\"name\"),\n            tornado.escape.to_unicode(hashed_password),\n        )\n        self.set_secure_cookie(\"blogdemo_user\", str(author.id))\n        self.redirect(self.get_argument(\"next\", \"/\"))\n\n\nclass AuthLoginHandler(BaseHandler):\n    async def get(self):\n        # If there are no authors, redirect to the account creation page.\n        if not await self.any_author_exists():\n            self.redirect(\"/auth/create\")\n        else:\n            self.render(\"login.html\", error=None)\n\n    async def post(self):\n        try:\n            author = await self.queryone(\n                \"SELECT * FROM authors WHERE email = %s\", self.get_argument(\"email\")\n            )\n        except NoResultError:\n            self.render(\"login.html\", error=\"email not found\")\n            return\n        password_equal = await tornado.ioloop.IOLoop.current().run_in_executor(\n            None,\n            bcrypt.checkpw,\n            tornado.escape.utf8(self.get_argument(\"password\")),\n            tornado.escape.utf8(author.hashed_password),\n        )\n        if password_equal:\n            self.set_secure_cookie(\"blogdemo_user\", str(author.id))\n            self.redirect(self.get_argument(\"next\", \"/\"))\n        else:\n            self.render(\"login.html\", error=\"incorrect password\")\n\n\nclass AuthLogoutHandler(BaseHandler):\n    def get(self):\n        self.clear_cookie(\"blogdemo_user\")\n        self.redirect(self.get_argument(\"next\", \"/\"))\n\n\nclass EntryModule(tornado.web.UIModule):\n    def render(self, entry):\n        return self.render_string(\"modules/entry.html\", entry=entry)\n\n\nasync def main():\n    tornado.options.parse_command_line()\n\n    # Create the global connection pool.\n    async with aiopg.create_pool(\n        host=options.db_host,\n        port=options.db_port,\n        user=options.db_user,\n        password=options.db_password,\n        dbname=options.db_database,\n    ) as db:\n        await maybe_create_tables(db)\n        app = Application(db)\n        app.listen(options.port)\n\n        # In this demo the server will simply run until interrupted\n        # with Ctrl-C, but if you want to shut down more gracefully,\n        # call shutdown_event.set().\n        shutdown_event = tornado.locks.Event()\n        await shutdown_event.wait()\n\n\nif __name__ == \"__main__\":\n    tornado.ioloop.IOLoop.current().run_sync(main)\n", "path": "demos/blog/blog.py"}]}