prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Support `shutdown` as a synonym for `cleanup`
  * Support either `cleanup` or `shutdown` as the ASGI lifespan message name.
  * Update uvicorn to move to shutdown - https://github.com/encode/uvicorn/issues/233
  * Finally, after a small period of time, drop `cleanup`
  
  Easy PR for a contributor to jump on would be addressing the first part of this, and supporting either name.
  
  </issue>
  <code>
  [start of starlette/lifespan.py]
  1 import asyncio
  2 import logging
  3 import traceback
  4 import typing
  5 from types import TracebackType
  6 from starlette.types import ASGIApp, ASGIInstance, Receive, Message, Send
  7 
  8 
  9 STATE_TRANSITION_ERROR = "Got invalid state transition on lifespan protocol."
  10 
  11 
  12 class LifespanHandler:
  13     def __init__(self) -> None:
  14         self.startup_handlers = []  # type: typing.List[typing.Callable]
  15         self.cleanup_handlers = []  # type: typing.List[typing.Callable]
  16 
  17     def on_event(self, event_type: str) -> typing.Callable:
  18         def decorator(func: typing.Callable) -> typing.Callable:
  19             self.add_event_handler(event_type, func)
  20             return func
  21 
  22         return decorator
  23 
  24     def add_event_handler(self, event_type: str, func: typing.Callable) -> None:
  25         assert event_type in ("startup", "cleanup")
  26 
  27         if event_type == "startup":
  28             self.startup_handlers.append(func)
  29         else:
  30             self.cleanup_handlers.append(func)
  31 
  32     async def run_startup(self) -> None:
  33         for handler in self.startup_handlers:
  34             if asyncio.iscoroutinefunction(handler):
  35                 await handler()
  36             else:
  37                 handler()
  38 
  39     async def run_cleanup(self) -> None:
  40         for handler in self.cleanup_handlers:
  41             if asyncio.iscoroutinefunction(handler):
  42                 await handler()
  43             else:
  44                 handler()
  45 
  46     def __call__(self, scope: Message) -> ASGIInstance:
  47         assert scope["type"] == "lifespan"
  48         return self.run_lifespan
  49 
  50     async def run_lifespan(self, receive: Receive, send: Send) -> None:
  51         message = await receive()
  52         assert message["type"] == "lifespan.startup"
  53         await self.run_startup()
  54         await send({"type": "lifespan.startup.complete"})
  55         message = await receive()
  56         assert message["type"] == "lifespan.cleanup"
  57         await self.run_cleanup()
  58         await send({"type": "lifespan.cleanup.complete"})
  59 
  60 
  61 class LifespanContext:
  62     def __init__(
  63         self, app: ASGIApp, startup_timeout: int = 10, cleanup_timeout: int = 10
  64     ) -> None:
  65         self.startup_timeout = startup_timeout
  66         self.cleanup_timeout = cleanup_timeout
  67         self.startup_event = asyncio.Event()
  68         self.cleanup_event = asyncio.Event()
  69         self.receive_queue = asyncio.Queue()  # type: asyncio.Queue
  70         self.asgi = app({"type": "lifespan"})  # type: ASGIInstance
  71 
  72     def __enter__(self) -> None:
  73         loop = asyncio.get_event_loop()
  74         loop.create_task(self.run_lifespan())
  75         loop.run_until_complete(self.wait_startup())
  76 
  77     def __exit__(
  78         self,
  79         exc_type: typing.Type[BaseException],
  80         exc: BaseException,
  81         tb: TracebackType,
  82     ) -> None:
  83         loop = asyncio.get_event_loop()
  84         loop.run_until_complete(self.wait_cleanup())
  85 
  86     async def run_lifespan(self) -> None:
  87         try:
  88             await self.asgi(self.receive, self.send)
  89         finally:
  90             self.startup_event.set()
  91             self.cleanup_event.set()
  92 
  93     async def send(self, message: Message) -> None:
  94         if message["type"] == "lifespan.startup.complete":
  95             assert not self.startup_event.is_set(), STATE_TRANSITION_ERROR
  96             assert not self.cleanup_event.is_set(), STATE_TRANSITION_ERROR
  97             self.startup_event.set()
  98         else:
  99             assert message["type"] == "lifespan.cleanup.complete"
  100             assert self.startup_event.is_set(), STATE_TRANSITION_ERROR
  101             assert not self.cleanup_event.is_set(), STATE_TRANSITION_ERROR
  102             self.cleanup_event.set()
  103 
  104     async def receive(self) -> Message:
  105         return await self.receive_queue.get()
  106 
  107     async def wait_startup(self) -> None:
  108         await self.receive_queue.put({"type": "lifespan.startup"})
  109         await asyncio.wait_for(self.startup_event.wait(), timeout=self.startup_timeout)
  110 
  111     async def wait_cleanup(self) -> None:
  112         await self.receive_queue.put({"type": "lifespan.cleanup"})
  113         await asyncio.wait_for(self.cleanup_event.wait(), timeout=self.cleanup_timeout)
  114
  [end of starlette/lifespan.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/starlette/lifespan.py b/starlette/lifespan.py
  --- a/starlette/lifespan.py
  +++ b/starlette/lifespan.py
  @@ -22,7 +22,7 @@
           return decorator
   
       def add_event_handler(self, event_type: str, func: typing.Callable) -> None:
  -        assert event_type in ("startup", "cleanup")
  +        assert event_type in ("startup", "shutdown", "cleanup")
   
           if event_type == "startup":
               self.startup_handlers.append(func)
  @@ -53,19 +53,26 @@
           await self.run_startup()
           await send({"type": "lifespan.startup.complete"})
           message = await receive()
  -        assert message["type"] == "lifespan.cleanup"
  +        assert (
  +            message["type"] == "lifespan.shutdown"
  +            or message["type"] == "lifespan.cleanup"
  +        )
           await self.run_cleanup()
  -        await send({"type": "lifespan.cleanup.complete"})
  +        if message["type"] == "lifespan.shutdown":
  +            await send({"type": "lifespan.shutdown.complete"})
  +
  +        if message["type"] == "lifespan.cleanup":
  +            await send({"type": "lifespan.cleanup.complete"})  # pragma: no cover
   
   
   class LifespanContext:
       def __init__(
  -        self, app: ASGIApp, startup_timeout: int = 10, cleanup_timeout: int = 10
  +        self, app: ASGIApp, startup_timeout: int = 10, shutdown_timeout: int = 10
       ) -> None:
           self.startup_timeout = startup_timeout
  -        self.cleanup_timeout = cleanup_timeout
  +        self.shutdown_timeout = shutdown_timeout
           self.startup_event = asyncio.Event()
  -        self.cleanup_event = asyncio.Event()
  +        self.shutdown_event = asyncio.Event()
           self.receive_queue = asyncio.Queue()  # type: asyncio.Queue
           self.asgi = app({"type": "lifespan"})  # type: ASGIInstance
   
  @@ -81,25 +88,25 @@
           tb: TracebackType,
       ) -> None:
           loop = asyncio.get_event_loop()
  -        loop.run_until_complete(self.wait_cleanup())
  +        loop.run_until_complete(self.wait_shutdown())
   
       async def run_lifespan(self) -> None:
           try:
               await self.asgi(self.receive, self.send)
           finally:
               self.startup_event.set()
  -            self.cleanup_event.set()
  +            self.shutdown_event.set()
   
       async def send(self, message: Message) -> None:
           if message["type"] == "lifespan.startup.complete":
               assert not self.startup_event.is_set(), STATE_TRANSITION_ERROR
  -            assert not self.cleanup_event.is_set(), STATE_TRANSITION_ERROR
  +            assert not self.shutdown_event.is_set(), STATE_TRANSITION_ERROR
               self.startup_event.set()
           else:
  -            assert message["type"] == "lifespan.cleanup.complete"
  +            assert message["type"] == "lifespan.shutdown.complete"
               assert self.startup_event.is_set(), STATE_TRANSITION_ERROR
  -            assert not self.cleanup_event.is_set(), STATE_TRANSITION_ERROR
  -            self.cleanup_event.set()
  +            assert not self.shutdown_event.is_set(), STATE_TRANSITION_ERROR
  +            self.shutdown_event.set()
   
       async def receive(self) -> Message:
           return await self.receive_queue.get()
  @@ -108,6 +115,8 @@
           await self.receive_queue.put({"type": "lifespan.startup"})
           await asyncio.wait_for(self.startup_event.wait(), timeout=self.startup_timeout)
   
  -    async def wait_cleanup(self) -> None:
  -        await self.receive_queue.put({"type": "lifespan.cleanup"})
  -        await asyncio.wait_for(self.cleanup_event.wait(), timeout=self.cleanup_timeout)
  +    async def wait_shutdown(self) -> None:
  +        await self.receive_queue.put({"type": "lifespan.shutdown"})
  +        await asyncio.wait_for(
  +            self.shutdown_event.wait(), timeout=self.shutdown_timeout
  +        )
  
verification_info: {"golden_diff": "diff --git a/starlette/lifespan.py b/starlette/lifespan.py\n--- a/starlette/lifespan.py\n+++ b/starlette/lifespan.py\n@@ -22,7 +22,7 @@\n         return decorator\n \n     def add_event_handler(self, event_type: str, func: typing.Callable) -> None:\n-        assert event_type in (\"startup\", \"cleanup\")\n+        assert event_type in (\"startup\", \"shutdown\", \"cleanup\")\n \n         if event_type == \"startup\":\n             self.startup_handlers.append(func)\n@@ -53,19 +53,26 @@\n         await self.run_startup()\n         await send({\"type\": \"lifespan.startup.complete\"})\n         message = await receive()\n-        assert message[\"type\"] == \"lifespan.cleanup\"\n+        assert (\n+            message[\"type\"] == \"lifespan.shutdown\"\n+            or message[\"type\"] == \"lifespan.cleanup\"\n+        )\n         await self.run_cleanup()\n-        await send({\"type\": \"lifespan.cleanup.complete\"})\n+        if message[\"type\"] == \"lifespan.shutdown\":\n+            await send({\"type\": \"lifespan.shutdown.complete\"})\n+\n+        if message[\"type\"] == \"lifespan.cleanup\":\n+            await send({\"type\": \"lifespan.cleanup.complete\"})  # pragma: no cover\n \n \n class LifespanContext:\n     def __init__(\n-        self, app: ASGIApp, startup_timeout: int = 10, cleanup_timeout: int = 10\n+        self, app: ASGIApp, startup_timeout: int = 10, shutdown_timeout: int = 10\n     ) -> None:\n         self.startup_timeout = startup_timeout\n-        self.cleanup_timeout = cleanup_timeout\n+        self.shutdown_timeout = shutdown_timeout\n         self.startup_event = asyncio.Event()\n-        self.cleanup_event = asyncio.Event()\n+        self.shutdown_event = asyncio.Event()\n         self.receive_queue = asyncio.Queue()  # type: asyncio.Queue\n         self.asgi = app({\"type\": \"lifespan\"})  # type: ASGIInstance\n \n@@ -81,25 +88,25 @@\n         tb: TracebackType,\n     ) -> None:\n         loop = asyncio.get_event_loop()\n-        loop.run_until_complete(self.wait_cleanup())\n+        loop.run_until_complete(self.wait_shutdown())\n \n     async def run_lifespan(self) -> None:\n         try:\n             await self.asgi(self.receive, self.send)\n         finally:\n             self.startup_event.set()\n-            self.cleanup_event.set()\n+            self.shutdown_event.set()\n \n     async def send(self, message: Message) -> None:\n         if message[\"type\"] == \"lifespan.startup.complete\":\n             assert not self.startup_event.is_set(), STATE_TRANSITION_ERROR\n-            assert not self.cleanup_event.is_set(), STATE_TRANSITION_ERROR\n+            assert not self.shutdown_event.is_set(), STATE_TRANSITION_ERROR\n             self.startup_event.set()\n         else:\n-            assert message[\"type\"] == \"lifespan.cleanup.complete\"\n+            assert message[\"type\"] == \"lifespan.shutdown.complete\"\n             assert self.startup_event.is_set(), STATE_TRANSITION_ERROR\n-            assert not self.cleanup_event.is_set(), STATE_TRANSITION_ERROR\n-            self.cleanup_event.set()\n+            assert not self.shutdown_event.is_set(), STATE_TRANSITION_ERROR\n+            self.shutdown_event.set()\n \n     async def receive(self) -> Message:\n         return await self.receive_queue.get()\n@@ -108,6 +115,8 @@\n         await self.receive_queue.put({\"type\": \"lifespan.startup\"})\n         await asyncio.wait_for(self.startup_event.wait(), timeout=self.startup_timeout)\n \n-    async def wait_cleanup(self) -> None:\n-        await self.receive_queue.put({\"type\": \"lifespan.cleanup\"})\n-        await asyncio.wait_for(self.cleanup_event.wait(), timeout=self.cleanup_timeout)\n+    async def wait_shutdown(self) -> None:\n+        await self.receive_queue.put({\"type\": \"lifespan.shutdown\"})\n+        await asyncio.wait_for(\n+            self.shutdown_event.wait(), timeout=self.shutdown_timeout\n+        )\n", "issue": "Support `shutdown` as a synonym for `cleanup`\n* Support either `cleanup` or `shutdown` as the ASGI lifespan message name.\r\n* Update uvicorn to move to shutdown - https://github.com/encode/uvicorn/issues/233\r\n* Finally, after a small period of time, drop `cleanup`\r\n\r\nEasy PR for a contributor to jump on would be addressing the first part of this, and supporting either name.\n", "before_files": [{"content": "import asyncio\nimport logging\nimport traceback\nimport typing\nfrom types import TracebackType\nfrom starlette.types import ASGIApp, ASGIInstance, Receive, Message, Send\n\n\nSTATE_TRANSITION_ERROR = \"Got invalid state transition on lifespan protocol.\"\n\n\nclass LifespanHandler:\n    def __init__(self) -> None:\n        self.startup_handlers = []  # type: typing.List[typing.Callable]\n        self.cleanup_handlers = []  # type: typing.List[typing.Callable]\n\n    def on_event(self, event_type: str) -> typing.Callable:\n        def decorator(func: typing.Callable) -> typing.Callable:\n            self.add_event_handler(event_type, func)\n            return func\n\n        return decorator\n\n    def add_event_handler(self, event_type: str, func: typing.Callable) -> None:\n        assert event_type in (\"startup\", \"cleanup\")\n\n        if event_type == \"startup\":\n            self.startup_handlers.append(func)\n        else:\n            self.cleanup_handlers.append(func)\n\n    async def run_startup(self) -> None:\n        for handler in self.startup_handlers:\n            if asyncio.iscoroutinefunction(handler):\n                await handler()\n            else:\n                handler()\n\n    async def run_cleanup(self) -> None:\n        for handler in self.cleanup_handlers:\n            if asyncio.iscoroutinefunction(handler):\n                await handler()\n            else:\n                handler()\n\n    def __call__(self, scope: Message) -> ASGIInstance:\n        assert scope[\"type\"] == \"lifespan\"\n        return self.run_lifespan\n\n    async def run_lifespan(self, receive: Receive, send: Send) -> None:\n        message = await receive()\n        assert message[\"type\"] == \"lifespan.startup\"\n        await self.run_startup()\n        await send({\"type\": \"lifespan.startup.complete\"})\n        message = await receive()\n        assert message[\"type\"] == \"lifespan.cleanup\"\n        await self.run_cleanup()\n        await send({\"type\": \"lifespan.cleanup.complete\"})\n\n\nclass LifespanContext:\n    def __init__(\n        self, app: ASGIApp, startup_timeout: int = 10, cleanup_timeout: int = 10\n    ) -> None:\n        self.startup_timeout = startup_timeout\n        self.cleanup_timeout = cleanup_timeout\n        self.startup_event = asyncio.Event()\n        self.cleanup_event = asyncio.Event()\n        self.receive_queue = asyncio.Queue()  # type: asyncio.Queue\n        self.asgi = app({\"type\": \"lifespan\"})  # type: ASGIInstance\n\n    def __enter__(self) -> None:\n        loop = asyncio.get_event_loop()\n        loop.create_task(self.run_lifespan())\n        loop.run_until_complete(self.wait_startup())\n\n    def __exit__(\n        self,\n        exc_type: typing.Type[BaseException],\n        exc: BaseException,\n        tb: TracebackType,\n    ) -> None:\n        loop = asyncio.get_event_loop()\n        loop.run_until_complete(self.wait_cleanup())\n\n    async def run_lifespan(self) -> None:\n        try:\n            await self.asgi(self.receive, self.send)\n        finally:\n            self.startup_event.set()\n            self.cleanup_event.set()\n\n    async def send(self, message: Message) -> None:\n        if message[\"type\"] == \"lifespan.startup.complete\":\n            assert not self.startup_event.is_set(), STATE_TRANSITION_ERROR\n            assert not self.cleanup_event.is_set(), STATE_TRANSITION_ERROR\n            self.startup_event.set()\n        else:\n            assert message[\"type\"] == \"lifespan.cleanup.complete\"\n            assert self.startup_event.is_set(), STATE_TRANSITION_ERROR\n            assert not self.cleanup_event.is_set(), STATE_TRANSITION_ERROR\n            self.cleanup_event.set()\n\n    async def receive(self) -> Message:\n        return await self.receive_queue.get()\n\n    async def wait_startup(self) -> None:\n        await self.receive_queue.put({\"type\": \"lifespan.startup\"})\n        await asyncio.wait_for(self.startup_event.wait(), timeout=self.startup_timeout)\n\n    async def wait_cleanup(self) -> None:\n        await self.receive_queue.put({\"type\": \"lifespan.cleanup\"})\n        await asyncio.wait_for(self.cleanup_event.wait(), timeout=self.cleanup_timeout)\n", "path": "starlette/lifespan.py"}]}