prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Warn about ignored buildHorizon and logHorizion.
  These aren't currently used, but they could be supported in the database. Either we should get rid of them, or open a ticket to add back support (and probably document that they aren't currently working).
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `master/buildbot/status/web/logs.py`
  Content:
  ```
  1 # This file is part of Buildbot.  Buildbot is free software: you can
  2 # redistribute it and/or modify it under the terms of the GNU General Public
  3 # License as published by the Free Software Foundation, version 2.
  4 #
  5 # This program is distributed in the hope that it will be useful, but WITHOUT
  6 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  7 # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
  8 # details.
  9 #
  10 # You should have received a copy of the GNU General Public License along with
  11 # this program; if not, write to the Free Software Foundation, Inc., 51
  12 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  13 #
  14 # Copyright Buildbot Team Members
  15 
  16 
  17 from zope.interface import implements
  18 from twisted.python import components
  19 from twisted.spread import pb
  20 from twisted.web import server
  21 from twisted.web.resource import Resource
  22 from twisted.web.error import NoResource
  23 
  24 from buildbot import interfaces
  25 from buildbot.status import logfile
  26 from buildbot.status.web.base import IHTMLLog, HtmlResource, path_to_root
  27 
  28 class ChunkConsumer:
  29     implements(interfaces.IStatusLogConsumer)
  30 
  31     def __init__(self, original, textlog):
  32         self.original = original
  33         self.textlog = textlog
  34     def registerProducer(self, producer, streaming):
  35         self.producer = producer
  36         self.original.registerProducer(producer, streaming)
  37     def unregisterProducer(self):
  38         self.original.unregisterProducer()
  39     def writeChunk(self, chunk):
  40         formatted = self.textlog.content([chunk])
  41         try:
  42             if isinstance(formatted, unicode):
  43                 formatted = formatted.encode('utf-8')
  44             self.original.write(formatted)
  45         except pb.DeadReferenceError:
  46             self.producing.stopProducing()
  47     def finish(self):
  48         self.textlog.finished()
  49 
  50 
  51 # /builders/$builder/builds/$buildnum/steps/$stepname/logs/$logname
  52 class TextLog(Resource):
  53     # a new instance of this Resource is created for each client who views
  54     # it, so we can afford to track the request in the Resource.
  55     implements(IHTMLLog)
  56 
  57     asText = False
  58     subscribed = False
  59 
  60     def __init__(self, original):
  61         Resource.__init__(self)
  62         self.original = original
  63 
  64     def getChild(self, path, req):
  65         if path == "text":
  66             self.asText = True
  67             return self
  68         return Resource.getChild(self, path, req)
  69 
  70     def content(self, entries):
  71         html_entries = []
  72         text_data = ''
  73         for type, entry in entries:
  74             if type >= len(logfile.ChunkTypes) or type < 0:
  75                 # non-std channel, don't display
  76                 continue
  77             
  78             is_header = type == logfile.HEADER
  79 
  80             if not self.asText:
  81                 # jinja only works with unicode, or pure ascii, so assume utf-8 in logs
  82                 if not isinstance(entry, unicode):
  83                     entry = unicode(entry, 'utf-8', 'replace')
  84                 html_entries.append(dict(type = logfile.ChunkTypes[type], 
  85                                          text = entry,
  86                                          is_header = is_header))
  87             elif not is_header:
  88                 text_data += entry
  89 
  90         if self.asText:
  91             return text_data
  92         else:
  93             return self.template.module.chunks(html_entries)
  94 
  95     def render_HEAD(self, req):
  96         self._setContentType(req)
  97 
  98         # vague approximation, ignores markup
  99         req.setHeader("content-length", self.original.length)
  100         return ''
  101 
  102     def render_GET(self, req):
  103         self._setContentType(req)
  104         self.req = req
  105 
  106         if not self.asText:
  107             self.template = req.site.buildbot_service.templates.get_template("logs.html")                
  108             
  109             data = self.template.module.page_header(
  110                     pageTitle = "Log File contents",
  111                     texturl = req.childLink("text"),
  112                     path_to_root = path_to_root(req))
  113             data = data.encode('utf-8')                   
  114             req.write(data)
  115 
  116         self.original.subscribeConsumer(ChunkConsumer(req, self))
  117         return server.NOT_DONE_YET
  118 
  119     def _setContentType(self, req):
  120         if self.asText:
  121             req.setHeader("content-type", "text/plain; charset=utf-8")
  122         else:
  123             req.setHeader("content-type", "text/html; charset=utf-8")
  124         
  125     def finished(self):
  126         if not self.req:
  127             return
  128         try:
  129             if not self.asText:
  130                 data = self.template.module.page_footer()
  131                 data = data.encode('utf-8')
  132                 self.req.write(data)
  133             self.req.finish()
  134         except pb.DeadReferenceError:
  135             pass
  136         # break the cycle, the Request's .notifications list includes the
  137         # Deferred (from req.notifyFinish) that's pointing at us.
  138         self.req = None
  139         
  140         # release template
  141         self.template = None
  142 
  143 components.registerAdapter(TextLog, interfaces.IStatusLog, IHTMLLog)
  144 
  145 
  146 class HTMLLog(Resource):
  147     implements(IHTMLLog)
  148 
  149     def __init__(self, original):
  150         Resource.__init__(self)
  151         self.original = original
  152 
  153     def render(self, request):
  154         request.setHeader("content-type", "text/html")
  155         return self.original.html
  156 
  157 components.registerAdapter(HTMLLog, logfile.HTMLLogFile, IHTMLLog)
  158 
  159 
  160 class LogsResource(HtmlResource):
  161     addSlash = True
  162 
  163     def __init__(self, step_status):
  164         HtmlResource.__init__(self)
  165         self.step_status = step_status
  166 
  167     def getChild(self, path, req):
  168         for log in self.step_status.getLogs():
  169             if path == log.getName():
  170                 if log.hasContents():
  171                     return IHTMLLog(interfaces.IStatusLog(log))
  172                 return NoResource("Empty Log '%s'" % path)
  173         return HtmlResource.getChild(self, path, req)
  174
  ```
  --- 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/master/buildbot/status/web/logs.py b/master/buildbot/status/web/logs.py
  --- a/master/buildbot/status/web/logs.py
  +++ b/master/buildbot/status/web/logs.py
  @@ -103,6 +103,11 @@
           self._setContentType(req)
           self.req = req
   
  +        if (self.original.isFinished()):
  +            req.setHeader("Cache-Control", "max-age=604800")
  +        else:
  +            req.setHeader("Cache-Control", "no-cache")
  +
           if not self.asText:
               self.template = req.site.buildbot_service.templates.get_template("logs.html")
  
verification_info: {"golden_diff": "diff --git a/master/buildbot/status/web/logs.py b/master/buildbot/status/web/logs.py\n--- a/master/buildbot/status/web/logs.py\n+++ b/master/buildbot/status/web/logs.py\n@@ -103,6 +103,11 @@\n         self._setContentType(req)\n         self.req = req\n \n+        if (self.original.isFinished()):\n+            req.setHeader(\"Cache-Control\", \"max-age=604800\")\n+        else:\n+            req.setHeader(\"Cache-Control\", \"no-cache\")\n+\n         if not self.asText:\n             self.template = req.site.buildbot_service.templates.get_template(\"logs.html\")\n", "issue": "Warn about ignored buildHorizon and logHorizion.\nThese aren't currently used, but they could be supported in the database. Either we should get rid of them, or open a ticket to add back support (and probably document that they aren't currently working).\n\n", "before_files": [{"content": "# This file is part of Buildbot.  Buildbot is free software: you can\n# redistribute it and/or modify it under the terms of the GNU General Public\n# License as published by the Free Software Foundation, version 2.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program; if not, write to the Free Software Foundation, Inc., 51\n# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n#\n# Copyright Buildbot Team Members\n\n\nfrom zope.interface import implements\nfrom twisted.python import components\nfrom twisted.spread import pb\nfrom twisted.web import server\nfrom twisted.web.resource import Resource\nfrom twisted.web.error import NoResource\n\nfrom buildbot import interfaces\nfrom buildbot.status import logfile\nfrom buildbot.status.web.base import IHTMLLog, HtmlResource, path_to_root\n\nclass ChunkConsumer:\n    implements(interfaces.IStatusLogConsumer)\n\n    def __init__(self, original, textlog):\n        self.original = original\n        self.textlog = textlog\n    def registerProducer(self, producer, streaming):\n        self.producer = producer\n        self.original.registerProducer(producer, streaming)\n    def unregisterProducer(self):\n        self.original.unregisterProducer()\n    def writeChunk(self, chunk):\n        formatted = self.textlog.content([chunk])\n        try:\n            if isinstance(formatted, unicode):\n                formatted = formatted.encode('utf-8')\n            self.original.write(formatted)\n        except pb.DeadReferenceError:\n            self.producing.stopProducing()\n    def finish(self):\n        self.textlog.finished()\n\n\n# /builders/$builder/builds/$buildnum/steps/$stepname/logs/$logname\nclass TextLog(Resource):\n    # a new instance of this Resource is created for each client who views\n    # it, so we can afford to track the request in the Resource.\n    implements(IHTMLLog)\n\n    asText = False\n    subscribed = False\n\n    def __init__(self, original):\n        Resource.__init__(self)\n        self.original = original\n\n    def getChild(self, path, req):\n        if path == \"text\":\n            self.asText = True\n            return self\n        return Resource.getChild(self, path, req)\n\n    def content(self, entries):\n        html_entries = []\n        text_data = ''\n        for type, entry in entries:\n            if type >= len(logfile.ChunkTypes) or type < 0:\n                # non-std channel, don't display\n                continue\n            \n            is_header = type == logfile.HEADER\n\n            if not self.asText:\n                # jinja only works with unicode, or pure ascii, so assume utf-8 in logs\n                if not isinstance(entry, unicode):\n                    entry = unicode(entry, 'utf-8', 'replace')\n                html_entries.append(dict(type = logfile.ChunkTypes[type], \n                                         text = entry,\n                                         is_header = is_header))\n            elif not is_header:\n                text_data += entry\n\n        if self.asText:\n            return text_data\n        else:\n            return self.template.module.chunks(html_entries)\n\n    def render_HEAD(self, req):\n        self._setContentType(req)\n\n        # vague approximation, ignores markup\n        req.setHeader(\"content-length\", self.original.length)\n        return ''\n\n    def render_GET(self, req):\n        self._setContentType(req)\n        self.req = req\n\n        if not self.asText:\n            self.template = req.site.buildbot_service.templates.get_template(\"logs.html\")                \n            \n            data = self.template.module.page_header(\n                    pageTitle = \"Log File contents\",\n                    texturl = req.childLink(\"text\"),\n                    path_to_root = path_to_root(req))\n            data = data.encode('utf-8')                   \n            req.write(data)\n\n        self.original.subscribeConsumer(ChunkConsumer(req, self))\n        return server.NOT_DONE_YET\n\n    def _setContentType(self, req):\n        if self.asText:\n            req.setHeader(\"content-type\", \"text/plain; charset=utf-8\")\n        else:\n            req.setHeader(\"content-type\", \"text/html; charset=utf-8\")\n        \n    def finished(self):\n        if not self.req:\n            return\n        try:\n            if not self.asText:\n                data = self.template.module.page_footer()\n                data = data.encode('utf-8')\n                self.req.write(data)\n            self.req.finish()\n        except pb.DeadReferenceError:\n            pass\n        # break the cycle, the Request's .notifications list includes the\n        # Deferred (from req.notifyFinish) that's pointing at us.\n        self.req = None\n        \n        # release template\n        self.template = None\n\ncomponents.registerAdapter(TextLog, interfaces.IStatusLog, IHTMLLog)\n\n\nclass HTMLLog(Resource):\n    implements(IHTMLLog)\n\n    def __init__(self, original):\n        Resource.__init__(self)\n        self.original = original\n\n    def render(self, request):\n        request.setHeader(\"content-type\", \"text/html\")\n        return self.original.html\n\ncomponents.registerAdapter(HTMLLog, logfile.HTMLLogFile, IHTMLLog)\n\n\nclass LogsResource(HtmlResource):\n    addSlash = True\n\n    def __init__(self, step_status):\n        HtmlResource.__init__(self)\n        self.step_status = step_status\n\n    def getChild(self, path, req):\n        for log in self.step_status.getLogs():\n            if path == log.getName():\n                if log.hasContents():\n                    return IHTMLLog(interfaces.IStatusLog(log))\n                return NoResource(\"Empty Log '%s'\" % path)\n        return HtmlResource.getChild(self, path, req)\n", "path": "master/buildbot/status/web/logs.py"}], "after_files": [{"content": "# This file is part of Buildbot.  Buildbot is free software: you can\n# redistribute it and/or modify it under the terms of the GNU General Public\n# License as published by the Free Software Foundation, version 2.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program; if not, write to the Free Software Foundation, Inc., 51\n# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n#\n# Copyright Buildbot Team Members\n\n\nfrom zope.interface import implements\nfrom twisted.python import components\nfrom twisted.spread import pb\nfrom twisted.web import server\nfrom twisted.web.resource import Resource\nfrom twisted.web.error import NoResource\n\nfrom buildbot import interfaces\nfrom buildbot.status import logfile\nfrom buildbot.status.web.base import IHTMLLog, HtmlResource, path_to_root\n\nclass ChunkConsumer:\n    implements(interfaces.IStatusLogConsumer)\n\n    def __init__(self, original, textlog):\n        self.original = original\n        self.textlog = textlog\n    def registerProducer(self, producer, streaming):\n        self.producer = producer\n        self.original.registerProducer(producer, streaming)\n    def unregisterProducer(self):\n        self.original.unregisterProducer()\n    def writeChunk(self, chunk):\n        formatted = self.textlog.content([chunk])\n        try:\n            if isinstance(formatted, unicode):\n                formatted = formatted.encode('utf-8')\n            self.original.write(formatted)\n        except pb.DeadReferenceError:\n            self.producing.stopProducing()\n    def finish(self):\n        self.textlog.finished()\n\n\n# /builders/$builder/builds/$buildnum/steps/$stepname/logs/$logname\nclass TextLog(Resource):\n    # a new instance of this Resource is created for each client who views\n    # it, so we can afford to track the request in the Resource.\n    implements(IHTMLLog)\n\n    asText = False\n    subscribed = False\n\n    def __init__(self, original):\n        Resource.__init__(self)\n        self.original = original\n\n    def getChild(self, path, req):\n        if path == \"text\":\n            self.asText = True\n            return self\n        return Resource.getChild(self, path, req)\n\n    def content(self, entries):\n        html_entries = []\n        text_data = ''\n        for type, entry in entries:\n            if type >= len(logfile.ChunkTypes) or type < 0:\n                # non-std channel, don't display\n                continue\n            \n            is_header = type == logfile.HEADER\n\n            if not self.asText:\n                # jinja only works with unicode, or pure ascii, so assume utf-8 in logs\n                if not isinstance(entry, unicode):\n                    entry = unicode(entry, 'utf-8', 'replace')\n                html_entries.append(dict(type = logfile.ChunkTypes[type], \n                                         text = entry,\n                                         is_header = is_header))\n            elif not is_header:\n                text_data += entry\n\n        if self.asText:\n            return text_data\n        else:\n            return self.template.module.chunks(html_entries)\n\n    def render_HEAD(self, req):\n        self._setContentType(req)\n\n        # vague approximation, ignores markup\n        req.setHeader(\"content-length\", self.original.length)\n        return ''\n\n    def render_GET(self, req):\n        self._setContentType(req)\n        self.req = req\n\n        if (self.original.isFinished()):\n            req.setHeader(\"Cache-Control\", \"max-age=604800\")\n        else:\n            req.setHeader(\"Cache-Control\", \"no-cache\")\n\n        if not self.asText:\n            self.template = req.site.buildbot_service.templates.get_template(\"logs.html\")                \n            \n            data = self.template.module.page_header(\n                    pageTitle = \"Log File contents\",\n                    texturl = req.childLink(\"text\"),\n                    path_to_root = path_to_root(req))\n            data = data.encode('utf-8')                   \n            req.write(data)\n\n        self.original.subscribeConsumer(ChunkConsumer(req, self))\n        return server.NOT_DONE_YET\n\n    def _setContentType(self, req):\n        if self.asText:\n            req.setHeader(\"content-type\", \"text/plain; charset=utf-8\")\n        else:\n            req.setHeader(\"content-type\", \"text/html; charset=utf-8\")\n        \n    def finished(self):\n        if not self.req:\n            return\n        try:\n            if not self.asText:\n                data = self.template.module.page_footer()\n                data = data.encode('utf-8')\n                self.req.write(data)\n            self.req.finish()\n        except pb.DeadReferenceError:\n            pass\n        # break the cycle, the Request's .notifications list includes the\n        # Deferred (from req.notifyFinish) that's pointing at us.\n        self.req = None\n        \n        # release template\n        self.template = None\n\ncomponents.registerAdapter(TextLog, interfaces.IStatusLog, IHTMLLog)\n\n\nclass HTMLLog(Resource):\n    implements(IHTMLLog)\n\n    def __init__(self, original):\n        Resource.__init__(self)\n        self.original = original\n\n    def render(self, request):\n        request.setHeader(\"content-type\", \"text/html\")\n        return self.original.html\n\ncomponents.registerAdapter(HTMLLog, logfile.HTMLLogFile, IHTMLLog)\n\n\nclass LogsResource(HtmlResource):\n    addSlash = True\n\n    def __init__(self, step_status):\n        HtmlResource.__init__(self)\n        self.step_status = step_status\n\n    def getChild(self, path, req):\n        for log in self.step_status.getLogs():\n            if path == log.getName():\n                if log.hasContents():\n                    return IHTMLLog(interfaces.IStatusLog(log))\n                return NoResource(\"Empty Log '%s'\" % path)\n        return HtmlResource.getChild(self, path, req)\n", "path": "master/buildbot/status/web/logs.py"}]}