prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Empty completion.timestamp_format crashes
  After `:set completion.timestamp_format ''`:
  
  ```
  17:26:29 ERROR    misc       crashsignal:exception_hook:216 Uncaught exception
  Traceback (most recent call last):
    File "/home/florian/proj/qutebrowser/git/qutebrowser/completion/completer.py", line 260, in _update_completion
      completion.set_pattern(pattern)
    File "/home/florian/proj/qutebrowser/git/qutebrowser/completion/completionwidget.py", line 320, in set_pattern
      self.model().set_pattern(pattern)
    File "/home/florian/proj/qutebrowser/git/qutebrowser/completion/models/completionmodel.py", line 185, in set_pattern
      cat.set_pattern(pattern)
    File "/home/florian/proj/qutebrowser/git/qutebrowser/completion/models/histcategory.py", line 85, in set_pattern
      .format(timestamp_format.replace("'", "`")))
  AttributeError: 'NoneType' object has no attribute 'replace'
  ```
  
  cc @rcorre and @erikdsjostrom who reported this
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `qutebrowser/completion/models/histcategory.py`
  Content:
  ```
  1 # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
  2 
  3 # Copyright 2017-2018 Ryan Roden-Corrent (rcorre) <ryan@rcorre.net>
  4 #
  5 # This file is part of qutebrowser.
  6 #
  7 # qutebrowser is free software: you can redistribute it and/or modify
  8 # it under the terms of the GNU General Public License as published by
  9 # the Free Software Foundation, either version 3 of the License, or
  10 # (at your option) any later version.
  11 #
  12 # qutebrowser is distributed in the hope that it will be useful,
  13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 # GNU General Public License for more details.
  16 #
  17 # You should have received a copy of the GNU General Public License
  18 # along with qutebrowser.  If not, see <http://www.gnu.org/licenses/>.
  19 
  20 """A completion category that queries the SQL History store."""
  21 
  22 from PyQt5.QtSql import QSqlQueryModel
  23 
  24 from qutebrowser.misc import sql
  25 from qutebrowser.utils import debug
  26 from qutebrowser.config import config
  27 
  28 
  29 class HistoryCategory(QSqlQueryModel):
  30 
  31     """A completion category that queries the SQL History store."""
  32 
  33     def __init__(self, *, delete_func=None, parent=None):
  34         """Create a new History completion category."""
  35         super().__init__(parent=parent)
  36         self.name = "History"
  37         self._query = None
  38 
  39         # advertise that this model filters by URL and title
  40         self.columns_to_filter = [0, 1]
  41         self.delete_func = delete_func
  42 
  43     def _atime_expr(self):
  44         """If max_items is set, return an expression to limit the query."""
  45         max_items = config.val.completion.web_history_max_items
  46         # HistoryCategory should not be added to the completion in that case.
  47         assert max_items != 0
  48 
  49         if max_items < 0:
  50             return ''
  51 
  52         min_atime = sql.Query(' '.join([
  53             'SELECT min(last_atime) FROM',
  54             '(SELECT last_atime FROM CompletionHistory',
  55             'ORDER BY last_atime DESC LIMIT :limit)',
  56         ])).run(limit=max_items).value()
  57 
  58         if not min_atime:
  59             # if there are no history items, min_atime may be '' (issue #2849)
  60             return ''
  61 
  62         return "AND last_atime >= {}".format(min_atime)
  63 
  64     def set_pattern(self, pattern):
  65         """Set the pattern used to filter results.
  66 
  67         Args:
  68             pattern: string pattern to filter by.
  69         """
  70         # escape to treat a user input % or _ as a literal, not a wildcard
  71         pattern = pattern.replace('%', '\\%')
  72         pattern = pattern.replace('_', '\\_')
  73         words = ['%{}%'.format(w) for w in pattern.split(' ')]
  74 
  75         # build a where clause to match all of the words in any order
  76         # given the search term "a b", the WHERE clause would be:
  77         # ((url || title) LIKE '%a%') AND ((url || title) LIKE '%b%')
  78         where_clause = ' AND '.join(
  79             "(url || title) LIKE :{} escape '\\'".format(i)
  80             for i in range(len(words)))
  81 
  82         # replace ' in timestamp-format to avoid breaking the query
  83         timestamp_format = config.val.completion.timestamp_format
  84         timefmt = ("strftime('{}', last_atime, 'unixepoch', 'localtime')"
  85                    .format(timestamp_format.replace("'", "`")))
  86 
  87         if not self._query or len(words) != len(self._query.boundValues()):
  88             # if the number of words changed, we need to generate a new query
  89             # otherwise, we can reuse the prepared query for performance
  90             self._query = sql.Query(' '.join([
  91                 "SELECT url, title, {}".format(timefmt),
  92                 "FROM CompletionHistory",
  93                 # the incoming pattern will have literal % and _ escaped
  94                 # we need to tell sql to treat '\' as an escape character
  95                 'WHERE ({})'.format(where_clause),
  96                 self._atime_expr(),
  97                 "ORDER BY last_atime DESC",
  98             ]), forward_only=False)
  99 
  100         with debug.log_time('sql', 'Running completion query'):
  101             self._query.run(**{
  102                 str(i): w for i, w in enumerate(words)})
  103         self.setQuery(self._query)
  104 
  105     def removeRows(self, row, _count, _parent=None):
  106         """Override QAbstractItemModel::removeRows to re-run sql query."""
  107         # re-run query to reload updated table
  108         with debug.log_time('sql', 'Re-running completion query post-delete'):
  109             self._query.run()
  110         self.setQuery(self._query)
  111         while self.rowCount() < row:
  112             self.fetchMore()
  113         return True
  114
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/qutebrowser/completion/models/histcategory.py b/qutebrowser/completion/models/histcategory.py
  --- a/qutebrowser/completion/models/histcategory.py
  +++ b/qutebrowser/completion/models/histcategory.py
  @@ -80,7 +80,7 @@
               for i in range(len(words)))
   
           # replace ' in timestamp-format to avoid breaking the query
  -        timestamp_format = config.val.completion.timestamp_format
  +        timestamp_format = config.val.completion.timestamp_format or ''
           timefmt = ("strftime('{}', last_atime, 'unixepoch', 'localtime')"
                      .format(timestamp_format.replace("'", "`")))
  
verification_info: {"golden_diff": "diff --git a/qutebrowser/completion/models/histcategory.py b/qutebrowser/completion/models/histcategory.py\n--- a/qutebrowser/completion/models/histcategory.py\n+++ b/qutebrowser/completion/models/histcategory.py\n@@ -80,7 +80,7 @@\n             for i in range(len(words)))\n \n         # replace ' in timestamp-format to avoid breaking the query\n-        timestamp_format = config.val.completion.timestamp_format\n+        timestamp_format = config.val.completion.timestamp_format or ''\n         timefmt = (\"strftime('{}', last_atime, 'unixepoch', 'localtime')\"\n                    .format(timestamp_format.replace(\"'\", \"`\")))\n", "issue": "Empty completion.timestamp_format crashes\nAfter `:set completion.timestamp_format ''`:\r\n\r\n```\r\n17:26:29 ERROR    misc       crashsignal:exception_hook:216 Uncaught exception\r\nTraceback (most recent call last):\r\n  File \"/home/florian/proj/qutebrowser/git/qutebrowser/completion/completer.py\", line 260, in _update_completion\r\n    completion.set_pattern(pattern)\r\n  File \"/home/florian/proj/qutebrowser/git/qutebrowser/completion/completionwidget.py\", line 320, in set_pattern\r\n    self.model().set_pattern(pattern)\r\n  File \"/home/florian/proj/qutebrowser/git/qutebrowser/completion/models/completionmodel.py\", line 185, in set_pattern\r\n    cat.set_pattern(pattern)\r\n  File \"/home/florian/proj/qutebrowser/git/qutebrowser/completion/models/histcategory.py\", line 85, in set_pattern\r\n    .format(timestamp_format.replace(\"'\", \"`\")))\r\nAttributeError: 'NoneType' object has no attribute 'replace'\r\n```\r\n\r\ncc @rcorre and @erikdsjostrom who reported this\n", "before_files": [{"content": "# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:\n\n# Copyright 2017-2018 Ryan Roden-Corrent (rcorre) <ryan@rcorre.net>\n#\n# This file is part of qutebrowser.\n#\n# qutebrowser is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# qutebrowser is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with qutebrowser.  If not, see <http://www.gnu.org/licenses/>.\n\n\"\"\"A completion category that queries the SQL History store.\"\"\"\n\nfrom PyQt5.QtSql import QSqlQueryModel\n\nfrom qutebrowser.misc import sql\nfrom qutebrowser.utils import debug\nfrom qutebrowser.config import config\n\n\nclass HistoryCategory(QSqlQueryModel):\n\n    \"\"\"A completion category that queries the SQL History store.\"\"\"\n\n    def __init__(self, *, delete_func=None, parent=None):\n        \"\"\"Create a new History completion category.\"\"\"\n        super().__init__(parent=parent)\n        self.name = \"History\"\n        self._query = None\n\n        # advertise that this model filters by URL and title\n        self.columns_to_filter = [0, 1]\n        self.delete_func = delete_func\n\n    def _atime_expr(self):\n        \"\"\"If max_items is set, return an expression to limit the query.\"\"\"\n        max_items = config.val.completion.web_history_max_items\n        # HistoryCategory should not be added to the completion in that case.\n        assert max_items != 0\n\n        if max_items < 0:\n            return ''\n\n        min_atime = sql.Query(' '.join([\n            'SELECT min(last_atime) FROM',\n            '(SELECT last_atime FROM CompletionHistory',\n            'ORDER BY last_atime DESC LIMIT :limit)',\n        ])).run(limit=max_items).value()\n\n        if not min_atime:\n            # if there are no history items, min_atime may be '' (issue #2849)\n            return ''\n\n        return \"AND last_atime >= {}\".format(min_atime)\n\n    def set_pattern(self, pattern):\n        \"\"\"Set the pattern used to filter results.\n\n        Args:\n            pattern: string pattern to filter by.\n        \"\"\"\n        # escape to treat a user input % or _ as a literal, not a wildcard\n        pattern = pattern.replace('%', '\\\\%')\n        pattern = pattern.replace('_', '\\\\_')\n        words = ['%{}%'.format(w) for w in pattern.split(' ')]\n\n        # build a where clause to match all of the words in any order\n        # given the search term \"a b\", the WHERE clause would be:\n        # ((url || title) LIKE '%a%') AND ((url || title) LIKE '%b%')\n        where_clause = ' AND '.join(\n            \"(url || title) LIKE :{} escape '\\\\'\".format(i)\n            for i in range(len(words)))\n\n        # replace ' in timestamp-format to avoid breaking the query\n        timestamp_format = config.val.completion.timestamp_format\n        timefmt = (\"strftime('{}', last_atime, 'unixepoch', 'localtime')\"\n                   .format(timestamp_format.replace(\"'\", \"`\")))\n\n        if not self._query or len(words) != len(self._query.boundValues()):\n            # if the number of words changed, we need to generate a new query\n            # otherwise, we can reuse the prepared query for performance\n            self._query = sql.Query(' '.join([\n                \"SELECT url, title, {}\".format(timefmt),\n                \"FROM CompletionHistory\",\n                # the incoming pattern will have literal % and _ escaped\n                # we need to tell sql to treat '\\' as an escape character\n                'WHERE ({})'.format(where_clause),\n                self._atime_expr(),\n                \"ORDER BY last_atime DESC\",\n            ]), forward_only=False)\n\n        with debug.log_time('sql', 'Running completion query'):\n            self._query.run(**{\n                str(i): w for i, w in enumerate(words)})\n        self.setQuery(self._query)\n\n    def removeRows(self, row, _count, _parent=None):\n        \"\"\"Override QAbstractItemModel::removeRows to re-run sql query.\"\"\"\n        # re-run query to reload updated table\n        with debug.log_time('sql', 'Re-running completion query post-delete'):\n            self._query.run()\n        self.setQuery(self._query)\n        while self.rowCount() < row:\n            self.fetchMore()\n        return True\n", "path": "qutebrowser/completion/models/histcategory.py"}], "after_files": [{"content": "# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:\n\n# Copyright 2017-2018 Ryan Roden-Corrent (rcorre) <ryan@rcorre.net>\n#\n# This file is part of qutebrowser.\n#\n# qutebrowser is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# qutebrowser is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with qutebrowser.  If not, see <http://www.gnu.org/licenses/>.\n\n\"\"\"A completion category that queries the SQL History store.\"\"\"\n\nfrom PyQt5.QtSql import QSqlQueryModel\n\nfrom qutebrowser.misc import sql\nfrom qutebrowser.utils import debug\nfrom qutebrowser.config import config\n\n\nclass HistoryCategory(QSqlQueryModel):\n\n    \"\"\"A completion category that queries the SQL History store.\"\"\"\n\n    def __init__(self, *, delete_func=None, parent=None):\n        \"\"\"Create a new History completion category.\"\"\"\n        super().__init__(parent=parent)\n        self.name = \"History\"\n        self._query = None\n\n        # advertise that this model filters by URL and title\n        self.columns_to_filter = [0, 1]\n        self.delete_func = delete_func\n\n    def _atime_expr(self):\n        \"\"\"If max_items is set, return an expression to limit the query.\"\"\"\n        max_items = config.val.completion.web_history_max_items\n        # HistoryCategory should not be added to the completion in that case.\n        assert max_items != 0\n\n        if max_items < 0:\n            return ''\n\n        min_atime = sql.Query(' '.join([\n            'SELECT min(last_atime) FROM',\n            '(SELECT last_atime FROM CompletionHistory',\n            'ORDER BY last_atime DESC LIMIT :limit)',\n        ])).run(limit=max_items).value()\n\n        if not min_atime:\n            # if there are no history items, min_atime may be '' (issue #2849)\n            return ''\n\n        return \"AND last_atime >= {}\".format(min_atime)\n\n    def set_pattern(self, pattern):\n        \"\"\"Set the pattern used to filter results.\n\n        Args:\n            pattern: string pattern to filter by.\n        \"\"\"\n        # escape to treat a user input % or _ as a literal, not a wildcard\n        pattern = pattern.replace('%', '\\\\%')\n        pattern = pattern.replace('_', '\\\\_')\n        words = ['%{}%'.format(w) for w in pattern.split(' ')]\n\n        # build a where clause to match all of the words in any order\n        # given the search term \"a b\", the WHERE clause would be:\n        # ((url || title) LIKE '%a%') AND ((url || title) LIKE '%b%')\n        where_clause = ' AND '.join(\n            \"(url || title) LIKE :{} escape '\\\\'\".format(i)\n            for i in range(len(words)))\n\n        # replace ' in timestamp-format to avoid breaking the query\n        timestamp_format = config.val.completion.timestamp_format or ''\n        timefmt = (\"strftime('{}', last_atime, 'unixepoch', 'localtime')\"\n                   .format(timestamp_format.replace(\"'\", \"`\")))\n\n        if not self._query or len(words) != len(self._query.boundValues()):\n            # if the number of words changed, we need to generate a new query\n            # otherwise, we can reuse the prepared query for performance\n            self._query = sql.Query(' '.join([\n                \"SELECT url, title, {}\".format(timefmt),\n                \"FROM CompletionHistory\",\n                # the incoming pattern will have literal % and _ escaped\n                # we need to tell sql to treat '\\' as an escape character\n                'WHERE ({})'.format(where_clause),\n                self._atime_expr(),\n                \"ORDER BY last_atime DESC\",\n            ]), forward_only=False)\n\n        with debug.log_time('sql', 'Running completion query'):\n            self._query.run(**{\n                str(i): w for i, w in enumerate(words)})\n        self.setQuery(self._query)\n\n    def removeRows(self, row, _count, _parent=None):\n        \"\"\"Override QAbstractItemModel::removeRows to re-run sql query.\"\"\"\n        # re-run query to reload updated table\n        with debug.log_time('sql', 'Re-running completion query post-delete'):\n            self._query.run()\n        self.setQuery(self._query)\n        while self.rowCount() < row:\n            self.fetchMore()\n        return True\n", "path": "qutebrowser/completion/models/histcategory.py"}]}