prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  lifespan.shutdown.failed message is not implemented
  there is no use that is made of the "lifespan.shutdown.failed" in the `async def send`. Is that an omission ? Readin the spec we should deal with it, not sure how yet, any ideas ?
  
  _Originally posted by @euri10 in https://github.com/encode/uvicorn/pull/751#issuecomment-674366666_
  
  </issue>
  <code>
  [start of uvicorn/lifespan/on.py]
  1 import asyncio
  2 import logging
  3 from asyncio import Queue
  4 
  5 from uvicorn import Config
  6 from uvicorn._types import LifespanReceiveMessage, LifespanScope, LifespanSendMessage
  7 
  8 STATE_TRANSITION_ERROR = "Got invalid state transition on lifespan protocol."
  9 
  10 
  11 class LifespanOn:
  12     def __init__(self, config: Config) -> None:
  13         if not config.loaded:
  14             config.load()
  15 
  16         self.config = config
  17         self.logger = logging.getLogger("uvicorn.error")
  18         self.startup_event = asyncio.Event()
  19         self.shutdown_event = asyncio.Event()
  20         self.receive_queue: "Queue[LifespanReceiveMessage]" = asyncio.Queue()
  21         self.error_occured = False
  22         self.startup_failed = False
  23         self.should_exit = False
  24 
  25     async def startup(self) -> None:
  26         self.logger.info("Waiting for application startup.")
  27 
  28         loop = asyncio.get_event_loop()
  29         loop.create_task(self.main())
  30 
  31         await self.receive_queue.put({"type": "lifespan.startup"})
  32         await self.startup_event.wait()
  33 
  34         if self.startup_failed or (self.error_occured and self.config.lifespan == "on"):
  35             self.logger.error("Application startup failed. Exiting.")
  36             self.should_exit = True
  37         else:
  38             self.logger.info("Application startup complete.")
  39 
  40     async def shutdown(self) -> None:
  41         if self.error_occured:
  42             return
  43         self.logger.info("Waiting for application shutdown.")
  44         await self.receive_queue.put({"type": "lifespan.shutdown"})
  45         await self.shutdown_event.wait()
  46         self.logger.info("Application shutdown complete.")
  47 
  48     async def main(self) -> None:
  49         try:
  50             app = self.config.loaded_app
  51             scope: LifespanScope = {
  52                 "type": "lifespan",
  53                 "asgi": {"version": self.config.asgi_version, "spec_version": "2.0"},
  54             }
  55             await app(scope, self.receive, self.send)
  56         except BaseException as exc:
  57             self.asgi = None
  58             self.error_occured = True
  59             if self.startup_failed:
  60                 return
  61             if self.config.lifespan == "auto":
  62                 msg = "ASGI 'lifespan' protocol appears unsupported."
  63                 self.logger.info(msg)
  64             else:
  65                 msg = "Exception in 'lifespan' protocol\n"
  66                 self.logger.error(msg, exc_info=exc)
  67         finally:
  68             self.startup_event.set()
  69             self.shutdown_event.set()
  70 
  71     async def send(self, message: LifespanSendMessage) -> None:
  72         assert message["type"] in (
  73             "lifespan.startup.complete",
  74             "lifespan.startup.failed",
  75             "lifespan.shutdown.complete",
  76         )
  77 
  78         if message["type"] == "lifespan.startup.complete":
  79             assert not self.startup_event.is_set(), STATE_TRANSITION_ERROR
  80             assert not self.shutdown_event.is_set(), STATE_TRANSITION_ERROR
  81             self.startup_event.set()
  82 
  83         elif message["type"] == "lifespan.startup.failed":
  84             assert not self.startup_event.is_set(), STATE_TRANSITION_ERROR
  85             assert not self.shutdown_event.is_set(), STATE_TRANSITION_ERROR
  86             self.startup_event.set()
  87             self.startup_failed = True
  88             if message.get("message"):
  89                 self.logger.error(message["message"])
  90 
  91         elif message["type"] == "lifespan.shutdown.complete":
  92             assert self.startup_event.is_set(), STATE_TRANSITION_ERROR
  93             assert not self.shutdown_event.is_set(), STATE_TRANSITION_ERROR
  94             self.shutdown_event.set()
  95 
  96     async def receive(self) -> LifespanReceiveMessage:
  97         return await self.receive_queue.get()
  98
  [end of uvicorn/lifespan/on.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/uvicorn/lifespan/on.py b/uvicorn/lifespan/on.py
  --- a/uvicorn/lifespan/on.py
  +++ b/uvicorn/lifespan/on.py
  @@ -20,6 +20,7 @@
           self.receive_queue: "Queue[LifespanReceiveMessage]" = asyncio.Queue()
           self.error_occured = False
           self.startup_failed = False
  +        self.shutdown_failed = False
           self.should_exit = False
   
       async def startup(self) -> None:
  @@ -43,7 +44,14 @@
           self.logger.info("Waiting for application shutdown.")
           await self.receive_queue.put({"type": "lifespan.shutdown"})
           await self.shutdown_event.wait()
  -        self.logger.info("Application shutdown complete.")
  +
  +        if self.shutdown_failed or (
  +            self.error_occured and self.config.lifespan == "on"
  +        ):
  +            self.logger.error("Application shutdown failed. Exiting.")
  +            self.should_exit = True
  +        else:
  +            self.logger.info("Application shutdown complete.")
   
       async def main(self) -> None:
           try:
  @@ -56,7 +64,7 @@
           except BaseException as exc:
               self.asgi = None
               self.error_occured = True
  -            if self.startup_failed:
  +            if self.startup_failed or self.shutdown_failed:
                   return
               if self.config.lifespan == "auto":
                   msg = "ASGI 'lifespan' protocol appears unsupported."
  @@ -73,6 +81,7 @@
               "lifespan.startup.complete",
               "lifespan.startup.failed",
               "lifespan.shutdown.complete",
  +            "lifespan.shutdown.failed",
           )
   
           if message["type"] == "lifespan.startup.complete":
  @@ -93,5 +102,13 @@
               assert not self.shutdown_event.is_set(), STATE_TRANSITION_ERROR
               self.shutdown_event.set()
   
  +        elif message["type"] == "lifespan.shutdown.failed":
  +            assert self.startup_event.is_set(), STATE_TRANSITION_ERROR
  +            assert not self.shutdown_event.is_set(), STATE_TRANSITION_ERROR
  +            self.shutdown_event.set()
  +            self.shutdown_failed = True
  +            if message.get("message"):
  +                self.logger.error(message["message"])
  +
       async def receive(self) -> LifespanReceiveMessage:
           return await self.receive_queue.get()
  
verification_info: {"golden_diff": "diff --git a/uvicorn/lifespan/on.py b/uvicorn/lifespan/on.py\n--- a/uvicorn/lifespan/on.py\n+++ b/uvicorn/lifespan/on.py\n@@ -20,6 +20,7 @@\n         self.receive_queue: \"Queue[LifespanReceiveMessage]\" = asyncio.Queue()\n         self.error_occured = False\n         self.startup_failed = False\n+        self.shutdown_failed = False\n         self.should_exit = False\n \n     async def startup(self) -> None:\n@@ -43,7 +44,14 @@\n         self.logger.info(\"Waiting for application shutdown.\")\n         await self.receive_queue.put({\"type\": \"lifespan.shutdown\"})\n         await self.shutdown_event.wait()\n-        self.logger.info(\"Application shutdown complete.\")\n+\n+        if self.shutdown_failed or (\n+            self.error_occured and self.config.lifespan == \"on\"\n+        ):\n+            self.logger.error(\"Application shutdown failed. Exiting.\")\n+            self.should_exit = True\n+        else:\n+            self.logger.info(\"Application shutdown complete.\")\n \n     async def main(self) -> None:\n         try:\n@@ -56,7 +64,7 @@\n         except BaseException as exc:\n             self.asgi = None\n             self.error_occured = True\n-            if self.startup_failed:\n+            if self.startup_failed or self.shutdown_failed:\n                 return\n             if self.config.lifespan == \"auto\":\n                 msg = \"ASGI 'lifespan' protocol appears unsupported.\"\n@@ -73,6 +81,7 @@\n             \"lifespan.startup.complete\",\n             \"lifespan.startup.failed\",\n             \"lifespan.shutdown.complete\",\n+            \"lifespan.shutdown.failed\",\n         )\n \n         if message[\"type\"] == \"lifespan.startup.complete\":\n@@ -93,5 +102,13 @@\n             assert not self.shutdown_event.is_set(), STATE_TRANSITION_ERROR\n             self.shutdown_event.set()\n \n+        elif message[\"type\"] == \"lifespan.shutdown.failed\":\n+            assert self.startup_event.is_set(), STATE_TRANSITION_ERROR\n+            assert not self.shutdown_event.is_set(), STATE_TRANSITION_ERROR\n+            self.shutdown_event.set()\n+            self.shutdown_failed = True\n+            if message.get(\"message\"):\n+                self.logger.error(message[\"message\"])\n+\n     async def receive(self) -> LifespanReceiveMessage:\n         return await self.receive_queue.get()\n", "issue": "lifespan.shutdown.failed message is not implemented\nthere is no use that is made of the \"lifespan.shutdown.failed\" in the `async def send`. Is that an omission ? Readin the spec we should deal with it, not sure how yet, any ideas ?\r\n\r\n_Originally posted by @euri10 in https://github.com/encode/uvicorn/pull/751#issuecomment-674366666_\n", "before_files": [{"content": "import asyncio\nimport logging\nfrom asyncio import Queue\n\nfrom uvicorn import Config\nfrom uvicorn._types import LifespanReceiveMessage, LifespanScope, LifespanSendMessage\n\nSTATE_TRANSITION_ERROR = \"Got invalid state transition on lifespan protocol.\"\n\n\nclass LifespanOn:\n    def __init__(self, config: Config) -> None:\n        if not config.loaded:\n            config.load()\n\n        self.config = config\n        self.logger = logging.getLogger(\"uvicorn.error\")\n        self.startup_event = asyncio.Event()\n        self.shutdown_event = asyncio.Event()\n        self.receive_queue: \"Queue[LifespanReceiveMessage]\" = asyncio.Queue()\n        self.error_occured = False\n        self.startup_failed = False\n        self.should_exit = False\n\n    async def startup(self) -> None:\n        self.logger.info(\"Waiting for application startup.\")\n\n        loop = asyncio.get_event_loop()\n        loop.create_task(self.main())\n\n        await self.receive_queue.put({\"type\": \"lifespan.startup\"})\n        await self.startup_event.wait()\n\n        if self.startup_failed or (self.error_occured and self.config.lifespan == \"on\"):\n            self.logger.error(\"Application startup failed. Exiting.\")\n            self.should_exit = True\n        else:\n            self.logger.info(\"Application startup complete.\")\n\n    async def shutdown(self) -> None:\n        if self.error_occured:\n            return\n        self.logger.info(\"Waiting for application shutdown.\")\n        await self.receive_queue.put({\"type\": \"lifespan.shutdown\"})\n        await self.shutdown_event.wait()\n        self.logger.info(\"Application shutdown complete.\")\n\n    async def main(self) -> None:\n        try:\n            app = self.config.loaded_app\n            scope: LifespanScope = {\n                \"type\": \"lifespan\",\n                \"asgi\": {\"version\": self.config.asgi_version, \"spec_version\": \"2.0\"},\n            }\n            await app(scope, self.receive, self.send)\n        except BaseException as exc:\n            self.asgi = None\n            self.error_occured = True\n            if self.startup_failed:\n                return\n            if self.config.lifespan == \"auto\":\n                msg = \"ASGI 'lifespan' protocol appears unsupported.\"\n                self.logger.info(msg)\n            else:\n                msg = \"Exception in 'lifespan' protocol\\n\"\n                self.logger.error(msg, exc_info=exc)\n        finally:\n            self.startup_event.set()\n            self.shutdown_event.set()\n\n    async def send(self, message: LifespanSendMessage) -> None:\n        assert message[\"type\"] in (\n            \"lifespan.startup.complete\",\n            \"lifespan.startup.failed\",\n            \"lifespan.shutdown.complete\",\n        )\n\n        if message[\"type\"] == \"lifespan.startup.complete\":\n            assert not self.startup_event.is_set(), STATE_TRANSITION_ERROR\n            assert not self.shutdown_event.is_set(), STATE_TRANSITION_ERROR\n            self.startup_event.set()\n\n        elif message[\"type\"] == \"lifespan.startup.failed\":\n            assert not self.startup_event.is_set(), STATE_TRANSITION_ERROR\n            assert not self.shutdown_event.is_set(), STATE_TRANSITION_ERROR\n            self.startup_event.set()\n            self.startup_failed = True\n            if message.get(\"message\"):\n                self.logger.error(message[\"message\"])\n\n        elif message[\"type\"] == \"lifespan.shutdown.complete\":\n            assert self.startup_event.is_set(), STATE_TRANSITION_ERROR\n            assert not self.shutdown_event.is_set(), STATE_TRANSITION_ERROR\n            self.shutdown_event.set()\n\n    async def receive(self) -> LifespanReceiveMessage:\n        return await self.receive_queue.get()\n", "path": "uvicorn/lifespan/on.py"}]}