We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Set event loop to Selector on windows
With the [3.8 python update](https://docs.python.org/3/whatsnew/3.8.html#asyncio), the default event loop on windows is now the `ProactorEventLoop`; one of aiohttp's dependencies uses features which are not supported in this event loop leading to crashes on startup on windows with an undescriptive error

```
From cffi callback <function _sock_state_cb at 0x0000000003816040>:
Traceback (most recent call last):
  File "C:\Users\Numerlor\.virtualenvs\bot-K_QdelnW\lib\site-packages\pycares\__init__.py", line 91, in _sock_state_cb
    sock_state_cb(socket_fd, readable, writable)
  File "C:\Users\Numerlor\.virtualenvs\bot-K_QdelnW\lib\site-packages\aiodns\__init__.py", line 105, in _sock_state_cb
    self.loop.add_reader(fd, self._handle_event, fd, READ)
  File "C:\Users\Numerlor\AppData\Local\Programs\Python\Python38\Lib\asyncio\events.py", line 501, in add_reader
    raise NotImplementedError
NotImplementedError
```
This can be fixed by setting the event loop policy to `WindowsSelectorEventLoopPolicy` through `asyncio.set_event_loop_policy` when the platform is detected to be windows

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `bot/bot.py`
Content:
```
1 import asyncio
2 import logging
3 import socket
4 import warnings
5 from typing import Optional
6 
7 import aiohttp
8 import discord
9 from discord.ext import commands
10 from sentry_sdk import push_scope
11 
12 from bot import DEBUG_MODE, api, constants
13 from bot.async_stats import AsyncStatsClient
14 
15 log = logging.getLogger('bot')
16 
17 
18 class Bot(commands.Bot):
19     """A subclass of `discord.ext.commands.Bot` with an aiohttp session and an API client."""
20 
21     def __init__(self, *args, **kwargs):
22         if "connector" in kwargs:
23             warnings.warn(
24                 "If login() is called (or the bot is started), the connector will be overwritten "
25                 "with an internal one"
26             )
27 
28         super().__init__(*args, **kwargs)
29 
30         self.http_session: Optional[aiohttp.ClientSession] = None
31         self.api_client = api.APIClient(loop=self.loop)
32 
33         self._connector = None
34         self._resolver = None
35         self._guild_available = asyncio.Event()
36 
37         statsd_url = constants.Stats.statsd_host
38 
39         if DEBUG_MODE:
40             # Since statsd is UDP, there are no errors for sending to a down port.
41             # For this reason, setting the statsd host to 127.0.0.1 for development
42             # will effectively disable stats.
43             statsd_url = "127.0.0.1"
44 
45         self.stats = AsyncStatsClient(self.loop, statsd_url, 8125, prefix="bot")
46 
47     def add_cog(self, cog: commands.Cog) -> None:
48         """Adds a "cog" to the bot and logs the operation."""
49         super().add_cog(cog)
50         log.info(f"Cog loaded: {cog.qualified_name}")
51 
52     def clear(self) -> None:
53         """
54         Clears the internal state of the bot and recreates the connector and sessions.
55 
56         Will cause a DeprecationWarning if called outside a coroutine.
57         """
58         # Because discord.py recreates the HTTPClient session, may as well follow suit and recreate
59         # our own stuff here too.
60         self._recreate()
61         super().clear()
62 
63     async def close(self) -> None:
64         """Close the Discord connection and the aiohttp session, connector, statsd client, and resolver."""
65         await super().close()
66 
67         await self.api_client.close()
68 
69         if self.http_session:
70             await self.http_session.close()
71 
72         if self._connector:
73             await self._connector.close()
74 
75         if self._resolver:
76             await self._resolver.close()
77 
78         if self.stats._transport:
79             await self.stats._transport.close()
80 
81     async def login(self, *args, **kwargs) -> None:
82         """Re-create the connector and set up sessions before logging into Discord."""
83         self._recreate()
84         await self.stats.create_socket()
85         await super().login(*args, **kwargs)
86 
87     def _recreate(self) -> None:
88         """Re-create the connector, aiohttp session, and the APIClient."""
89         # Use asyncio for DNS resolution instead of threads so threads aren't spammed.
90         # Doesn't seem to have any state with regards to being closed, so no need to worry?
91         self._resolver = aiohttp.AsyncResolver()
92 
93         # Its __del__ does send a warning but it doesn't always show up for some reason.
94         if self._connector and not self._connector._closed:
95             log.warning(
96                 "The previous connector was not closed; it will remain open and be overwritten"
97             )
98 
99         # Use AF_INET as its socket family to prevent HTTPS related problems both locally
100         # and in production.
101         self._connector = aiohttp.TCPConnector(
102             resolver=self._resolver,
103             family=socket.AF_INET,
104         )
105 
106         # Client.login() will call HTTPClient.static_login() which will create a session using
107         # this connector attribute.
108         self.http.connector = self._connector
109 
110         # Its __del__ does send a warning but it doesn't always show up for some reason.
111         if self.http_session and not self.http_session.closed:
112             log.warning(
113                 "The previous session was not closed; it will remain open and be overwritten"
114             )
115 
116         self.http_session = aiohttp.ClientSession(connector=self._connector)
117         self.api_client.recreate(force=True, connector=self._connector)
118 
119     async def on_guild_available(self, guild: discord.Guild) -> None:
120         """
121         Set the internal guild available event when constants.Guild.id becomes available.
122 
123         If the cache appears to still be empty (no members, no channels, or no roles), the event
124         will not be set.
125         """
126         if guild.id != constants.Guild.id:
127             return
128 
129         if not guild.roles or not guild.members or not guild.channels:
130             msg = "Guild available event was dispatched but the cache appears to still be empty!"
131             log.warning(msg)
132 
133             try:
134                 webhook = await self.fetch_webhook(constants.Webhooks.dev_log)
135             except discord.HTTPException as e:
136                 log.error(f"Failed to fetch webhook to send empty cache warning: status {e.status}")
137             else:
138                 await webhook.send(f"<@&{constants.Roles.admin}> {msg}")
139 
140             return
141 
142         self._guild_available.set()
143 
144     async def on_guild_unavailable(self, guild: discord.Guild) -> None:
145         """Clear the internal guild available event when constants.Guild.id becomes unavailable."""
146         if guild.id != constants.Guild.id:
147             return
148 
149         self._guild_available.clear()
150 
151     async def wait_until_guild_available(self) -> None:
152         """
153         Wait until the constants.Guild.id guild is available (and the cache is ready).
154 
155         The on_ready event is inadequate because it only waits 2 seconds for a GUILD_CREATE
156         gateway event before giving up and thus not populating the cache for unavailable guilds.
157         """
158         await self._guild_available.wait()
159 
160     async def on_error(self, event: str, *args, **kwargs) -> None:
161         """Log errors raised in event listeners rather than printing them to stderr."""
162         self.stats.incr(f"errors.event.{event}")
163 
164         with push_scope() as scope:
165             scope.set_tag("event", event)
166             scope.set_extra("args", args)
167             scope.set_extra("kwargs", kwargs)
168 
169             log.exception(f"Unhandled exception in {event}.")
170
```

Path: `bot/__init__.py`
Content:
```
1 import logging
2 import os
3 import sys
4 from logging import Logger, handlers
5 from pathlib import Path
6 
7 import coloredlogs
8 
9 TRACE_LEVEL = logging.TRACE = 5
10 logging.addLevelName(TRACE_LEVEL, "TRACE")
11 
12 
13 def monkeypatch_trace(self: logging.Logger, msg: str, *args, **kwargs) -> None:
14     """
15     Log 'msg % args' with severity 'TRACE'.
16 
17     To pass exception information, use the keyword argument exc_info with
18     a true value, e.g.
19 
20     logger.trace("Houston, we have an %s", "interesting problem", exc_info=1)
21     """
22     if self.isEnabledFor(TRACE_LEVEL):
23         self._log(TRACE_LEVEL, msg, args, **kwargs)
24 
25 
26 Logger.trace = monkeypatch_trace
27 
28 DEBUG_MODE = 'local' in os.environ.get("SITE_URL", "local")
29 
30 log_level = TRACE_LEVEL if DEBUG_MODE else logging.INFO
31 format_string = "%(asctime)s | %(name)s | %(levelname)s | %(message)s"
32 log_format = logging.Formatter(format_string)
33 
34 log_file = Path("logs", "bot.log")
35 log_file.parent.mkdir(exist_ok=True)
36 file_handler = handlers.RotatingFileHandler(log_file, maxBytes=5242880, backupCount=7, encoding="utf8")
37 file_handler.setFormatter(log_format)
38 
39 root_log = logging.getLogger()
40 root_log.setLevel(log_level)
41 root_log.addHandler(file_handler)
42 
43 if "COLOREDLOGS_LEVEL_STYLES" not in os.environ:
44     coloredlogs.DEFAULT_LEVEL_STYLES = {
45         **coloredlogs.DEFAULT_LEVEL_STYLES,
46         "trace": {"color": 246},
47         "critical": {"background": "red"},
48         "debug": coloredlogs.DEFAULT_LEVEL_STYLES["info"]
49     }
50 
51 if "COLOREDLOGS_LOG_FORMAT" not in os.environ:
52     coloredlogs.DEFAULT_LOG_FORMAT = format_string
53 
54 if "COLOREDLOGS_LOG_LEVEL" not in os.environ:
55     coloredlogs.DEFAULT_LOG_LEVEL = log_level
56 
57 coloredlogs.install(logger=root_log, stream=sys.stdout)
58 
59 logging.getLogger("discord").setLevel(logging.WARNING)
60 logging.getLogger("websockets").setLevel(logging.WARNING)
61 logging.getLogger(__name__)
62
```
--- 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/bot/__init__.py b/bot/__init__.py
--- a/bot/__init__.py
+++ b/bot/__init__.py
@@ -1,3 +1,4 @@
+import asyncio
 import logging
 import os
 import sys
@@ -59,3 +60,8 @@
 logging.getLogger("discord").setLevel(logging.WARNING)
 logging.getLogger("websockets").setLevel(logging.WARNING)
 logging.getLogger(__name__)
+
+
+# On Windows, the selector event loop is required for aiodns.
+if os.name == "nt":
+    asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
diff --git a/bot/bot.py b/bot/bot.py
--- a/bot/bot.py
+++ b/bot/bot.py
@@ -76,7 +76,7 @@
             await self._resolver.close()
 
         if self.stats._transport:
-            await self.stats._transport.close()
+            self.stats._transport.close()
 
     async def login(self, *args, **kwargs) -> None:
         """Re-create the connector and set up sessions before logging into Discord."""

{"golden_diff": "diff --git a/bot/__init__.py b/bot/__init__.py\n--- a/bot/__init__.py\n+++ b/bot/__init__.py\n@@ -1,3 +1,4 @@\n+import asyncio\n import logging\n import os\n import sys\n@@ -59,3 +60,8 @@\n logging.getLogger(\"discord\").setLevel(logging.WARNING)\n logging.getLogger(\"websockets\").setLevel(logging.WARNING)\n logging.getLogger(__name__)\n+\n+\n+# On Windows, the selector event loop is required for aiodns.\n+if os.name == \"nt\":\n+    asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())\ndiff --git a/bot/bot.py b/bot/bot.py\n--- a/bot/bot.py\n+++ b/bot/bot.py\n@@ -76,7 +76,7 @@\n             await self._resolver.close()\n \n         if self.stats._transport:\n-            await self.stats._transport.close()\n+            self.stats._transport.close()\n \n     async def login(self, *args, **kwargs) -> None:\n         \"\"\"Re-create the connector and set up sessions before logging into Discord.\"\"\"\n", "issue": "Set event loop to Selector on windows\nWith the [3.8 python update](https://docs.python.org/3/whatsnew/3.8.html#asyncio), the default event loop on windows is now the `ProactorEventLoop`; one of aiohttp's dependencies uses features which are not supported in this event loop leading to crashes on startup on windows with an undescriptive error\r\n\r\n```\r\nFrom cffi callback <function _sock_state_cb at 0x0000000003816040>:\r\nTraceback (most recent call last):\r\n  File \"C:\\Users\\Numerlor\\.virtualenvs\\bot-K_QdelnW\\lib\\site-packages\\pycares\\__init__.py\", line 91, in _sock_state_cb\r\n    sock_state_cb(socket_fd, readable, writable)\r\n  File \"C:\\Users\\Numerlor\\.virtualenvs\\bot-K_QdelnW\\lib\\site-packages\\aiodns\\__init__.py\", line 105, in _sock_state_cb\r\n    self.loop.add_reader(fd, self._handle_event, fd, READ)\r\n  File \"C:\\Users\\Numerlor\\AppData\\Local\\Programs\\Python\\Python38\\Lib\\asyncio\\events.py\", line 501, in add_reader\r\n    raise NotImplementedError\r\nNotImplementedError\r\n```\r\nThis can be fixed by setting the event loop policy to `WindowsSelectorEventLoopPolicy` through `asyncio.set_event_loop_policy` when the platform is detected to be windows\n", "before_files": [{"content": "import asyncio\nimport logging\nimport socket\nimport warnings\nfrom typing import Optional\n\nimport aiohttp\nimport discord\nfrom discord.ext import commands\nfrom sentry_sdk import push_scope\n\nfrom bot import DEBUG_MODE, api, constants\nfrom bot.async_stats import AsyncStatsClient\n\nlog = logging.getLogger('bot')\n\n\nclass Bot(commands.Bot):\n    \"\"\"A subclass of `discord.ext.commands.Bot` with an aiohttp session and an API client.\"\"\"\n\n    def __init__(self, *args, **kwargs):\n        if \"connector\" in kwargs:\n            warnings.warn(\n                \"If login() is called (or the bot is started), the connector will be overwritten \"\n                \"with an internal one\"\n            )\n\n        super().__init__(*args, **kwargs)\n\n        self.http_session: Optional[aiohttp.ClientSession] = None\n        self.api_client = api.APIClient(loop=self.loop)\n\n        self._connector = None\n        self._resolver = None\n        self._guild_available = asyncio.Event()\n\n        statsd_url = constants.Stats.statsd_host\n\n        if DEBUG_MODE:\n            # Since statsd is UDP, there are no errors for sending to a down port.\n            # For this reason, setting the statsd host to 127.0.0.1 for development\n            # will effectively disable stats.\n            statsd_url = \"127.0.0.1\"\n\n        self.stats = AsyncStatsClient(self.loop, statsd_url, 8125, prefix=\"bot\")\n\n    def add_cog(self, cog: commands.Cog) -> None:\n        \"\"\"Adds a \"cog\" to the bot and logs the operation.\"\"\"\n        super().add_cog(cog)\n        log.info(f\"Cog loaded: {cog.qualified_name}\")\n\n    def clear(self) -> None:\n        \"\"\"\n        Clears the internal state of the bot and recreates the connector and sessions.\n\n        Will cause a DeprecationWarning if called outside a coroutine.\n        \"\"\"\n        # Because discord.py recreates the HTTPClient session, may as well follow suit and recreate\n        # our own stuff here too.\n        self._recreate()\n        super().clear()\n\n    async def close(self) -> None:\n        \"\"\"Close the Discord connection and the aiohttp session, connector, statsd client, and resolver.\"\"\"\n        await super().close()\n\n        await self.api_client.close()\n\n        if self.http_session:\n            await self.http_session.close()\n\n        if self._connector:\n            await self._connector.close()\n\n        if self._resolver:\n            await self._resolver.close()\n\n        if self.stats._transport:\n            await self.stats._transport.close()\n\n    async def login(self, *args, **kwargs) -> None:\n        \"\"\"Re-create the connector and set up sessions before logging into Discord.\"\"\"\n        self._recreate()\n        await self.stats.create_socket()\n        await super().login(*args, **kwargs)\n\n    def _recreate(self) -> None:\n        \"\"\"Re-create the connector, aiohttp session, and the APIClient.\"\"\"\n        # Use asyncio for DNS resolution instead of threads so threads aren't spammed.\n        # Doesn't seem to have any state with regards to being closed, so no need to worry?\n        self._resolver = aiohttp.AsyncResolver()\n\n        # Its __del__ does send a warning but it doesn't always show up for some reason.\n        if self._connector and not self._connector._closed:\n            log.warning(\n                \"The previous connector was not closed; it will remain open and be overwritten\"\n            )\n\n        # Use AF_INET as its socket family to prevent HTTPS related problems both locally\n        # and in production.\n        self._connector = aiohttp.TCPConnector(\n            resolver=self._resolver,\n            family=socket.AF_INET,\n        )\n\n        # Client.login() will call HTTPClient.static_login() which will create a session using\n        # this connector attribute.\n        self.http.connector = self._connector\n\n        # Its __del__ does send a warning but it doesn't always show up for some reason.\n        if self.http_session and not self.http_session.closed:\n            log.warning(\n                \"The previous session was not closed; it will remain open and be overwritten\"\n            )\n\n        self.http_session = aiohttp.ClientSession(connector=self._connector)\n        self.api_client.recreate(force=True, connector=self._connector)\n\n    async def on_guild_available(self, guild: discord.Guild) -> None:\n        \"\"\"\n        Set the internal guild available event when constants.Guild.id becomes available.\n\n        If the cache appears to still be empty (no members, no channels, or no roles), the event\n        will not be set.\n        \"\"\"\n        if guild.id != constants.Guild.id:\n            return\n\n        if not guild.roles or not guild.members or not guild.channels:\n            msg = \"Guild available event was dispatched but the cache appears to still be empty!\"\n            log.warning(msg)\n\n            try:\n                webhook = await self.fetch_webhook(constants.Webhooks.dev_log)\n            except discord.HTTPException as e:\n                log.error(f\"Failed to fetch webhook to send empty cache warning: status {e.status}\")\n            else:\n                await webhook.send(f\"<@&{constants.Roles.admin}> {msg}\")\n\n            return\n\n        self._guild_available.set()\n\n    async def on_guild_unavailable(self, guild: discord.Guild) -> None:\n        \"\"\"Clear the internal guild available event when constants.Guild.id becomes unavailable.\"\"\"\n        if guild.id != constants.Guild.id:\n            return\n\n        self._guild_available.clear()\n\n    async def wait_until_guild_available(self) -> None:\n        \"\"\"\n        Wait until the constants.Guild.id guild is available (and the cache is ready).\n\n        The on_ready event is inadequate because it only waits 2 seconds for a GUILD_CREATE\n        gateway event before giving up and thus not populating the cache for unavailable guilds.\n        \"\"\"\n        await self._guild_available.wait()\n\n    async def on_error(self, event: str, *args, **kwargs) -> None:\n        \"\"\"Log errors raised in event listeners rather than printing them to stderr.\"\"\"\n        self.stats.incr(f\"errors.event.{event}\")\n\n        with push_scope() as scope:\n            scope.set_tag(\"event\", event)\n            scope.set_extra(\"args\", args)\n            scope.set_extra(\"kwargs\", kwargs)\n\n            log.exception(f\"Unhandled exception in {event}.\")\n", "path": "bot/bot.py"}, {"content": "import logging\nimport os\nimport sys\nfrom logging import Logger, handlers\nfrom pathlib import Path\n\nimport coloredlogs\n\nTRACE_LEVEL = logging.TRACE = 5\nlogging.addLevelName(TRACE_LEVEL, \"TRACE\")\n\n\ndef monkeypatch_trace(self: logging.Logger, msg: str, *args, **kwargs) -> None:\n    \"\"\"\n    Log 'msg % args' with severity 'TRACE'.\n\n    To pass exception information, use the keyword argument exc_info with\n    a true value, e.g.\n\n    logger.trace(\"Houston, we have an %s\", \"interesting problem\", exc_info=1)\n    \"\"\"\n    if self.isEnabledFor(TRACE_LEVEL):\n        self._log(TRACE_LEVEL, msg, args, **kwargs)\n\n\nLogger.trace = monkeypatch_trace\n\nDEBUG_MODE = 'local' in os.environ.get(\"SITE_URL\", \"local\")\n\nlog_level = TRACE_LEVEL if DEBUG_MODE else logging.INFO\nformat_string = \"%(asctime)s | %(name)s | %(levelname)s | %(message)s\"\nlog_format = logging.Formatter(format_string)\n\nlog_file = Path(\"logs\", \"bot.log\")\nlog_file.parent.mkdir(exist_ok=True)\nfile_handler = handlers.RotatingFileHandler(log_file, maxBytes=5242880, backupCount=7, encoding=\"utf8\")\nfile_handler.setFormatter(log_format)\n\nroot_log = logging.getLogger()\nroot_log.setLevel(log_level)\nroot_log.addHandler(file_handler)\n\nif \"COLOREDLOGS_LEVEL_STYLES\" not in os.environ:\n    coloredlogs.DEFAULT_LEVEL_STYLES = {\n        **coloredlogs.DEFAULT_LEVEL_STYLES,\n        \"trace\": {\"color\": 246},\n        \"critical\": {\"background\": \"red\"},\n        \"debug\": coloredlogs.DEFAULT_LEVEL_STYLES[\"info\"]\n    }\n\nif \"COLOREDLOGS_LOG_FORMAT\" not in os.environ:\n    coloredlogs.DEFAULT_LOG_FORMAT = format_string\n\nif \"COLOREDLOGS_LOG_LEVEL\" not in os.environ:\n    coloredlogs.DEFAULT_LOG_LEVEL = log_level\n\ncoloredlogs.install(logger=root_log, stream=sys.stdout)\n\nlogging.getLogger(\"discord\").setLevel(logging.WARNING)\nlogging.getLogger(\"websockets\").setLevel(logging.WARNING)\nlogging.getLogger(__name__)\n", "path": "bot/__init__.py"}], "after_files": [{"content": "import asyncio\nimport logging\nimport socket\nimport warnings\nfrom typing import Optional\n\nimport aiohttp\nimport discord\nfrom discord.ext import commands\nfrom sentry_sdk import push_scope\n\nfrom bot import DEBUG_MODE, api, constants\nfrom bot.async_stats import AsyncStatsClient\n\nlog = logging.getLogger('bot')\n\n\nclass Bot(commands.Bot):\n    \"\"\"A subclass of `discord.ext.commands.Bot` with an aiohttp session and an API client.\"\"\"\n\n    def __init__(self, *args, **kwargs):\n        if \"connector\" in kwargs:\n            warnings.warn(\n                \"If login() is called (or the bot is started), the connector will be overwritten \"\n                \"with an internal one\"\n            )\n\n        super().__init__(*args, **kwargs)\n\n        self.http_session: Optional[aiohttp.ClientSession] = None\n        self.api_client = api.APIClient(loop=self.loop)\n\n        self._connector = None\n        self._resolver = None\n        self._guild_available = asyncio.Event()\n\n        statsd_url = constants.Stats.statsd_host\n\n        if DEBUG_MODE:\n            # Since statsd is UDP, there are no errors for sending to a down port.\n            # For this reason, setting the statsd host to 127.0.0.1 for development\n            # will effectively disable stats.\n            statsd_url = \"127.0.0.1\"\n\n        self.stats = AsyncStatsClient(self.loop, statsd_url, 8125, prefix=\"bot\")\n\n    def add_cog(self, cog: commands.Cog) -> None:\n        \"\"\"Adds a \"cog\" to the bot and logs the operation.\"\"\"\n        super().add_cog(cog)\n        log.info(f\"Cog loaded: {cog.qualified_name}\")\n\n    def clear(self) -> None:\n        \"\"\"\n        Clears the internal state of the bot and recreates the connector and sessions.\n\n        Will cause a DeprecationWarning if called outside a coroutine.\n        \"\"\"\n        # Because discord.py recreates the HTTPClient session, may as well follow suit and recreate\n        # our own stuff here too.\n        self._recreate()\n        super().clear()\n\n    async def close(self) -> None:\n        \"\"\"Close the Discord connection and the aiohttp session, connector, statsd client, and resolver.\"\"\"\n        await super().close()\n\n        await self.api_client.close()\n\n        if self.http_session:\n            await self.http_session.close()\n\n        if self._connector:\n            await self._connector.close()\n\n        if self._resolver:\n            await self._resolver.close()\n\n        if self.stats._transport:\n            self.stats._transport.close()\n\n    async def login(self, *args, **kwargs) -> None:\n        \"\"\"Re-create the connector and set up sessions before logging into Discord.\"\"\"\n        self._recreate()\n        await self.stats.create_socket()\n        await super().login(*args, **kwargs)\n\n    def _recreate(self) -> None:\n        \"\"\"Re-create the connector, aiohttp session, and the APIClient.\"\"\"\n        # Use asyncio for DNS resolution instead of threads so threads aren't spammed.\n        # Doesn't seem to have any state with regards to being closed, so no need to worry?\n        self._resolver = aiohttp.AsyncResolver()\n\n        # Its __del__ does send a warning but it doesn't always show up for some reason.\n        if self._connector and not self._connector._closed:\n            log.warning(\n                \"The previous connector was not closed; it will remain open and be overwritten\"\n            )\n\n        # Use AF_INET as its socket family to prevent HTTPS related problems both locally\n        # and in production.\n        self._connector = aiohttp.TCPConnector(\n            resolver=self._resolver,\n            family=socket.AF_INET,\n        )\n\n        # Client.login() will call HTTPClient.static_login() which will create a session using\n        # this connector attribute.\n        self.http.connector = self._connector\n\n        # Its __del__ does send a warning but it doesn't always show up for some reason.\n        if self.http_session and not self.http_session.closed:\n            log.warning(\n                \"The previous session was not closed; it will remain open and be overwritten\"\n            )\n\n        self.http_session = aiohttp.ClientSession(connector=self._connector)\n        self.api_client.recreate(force=True, connector=self._connector)\n\n    async def on_guild_available(self, guild: discord.Guild) -> None:\n        \"\"\"\n        Set the internal guild available event when constants.Guild.id becomes available.\n\n        If the cache appears to still be empty (no members, no channels, or no roles), the event\n        will not be set.\n        \"\"\"\n        if guild.id != constants.Guild.id:\n            return\n\n        if not guild.roles or not guild.members or not guild.channels:\n            msg = \"Guild available event was dispatched but the cache appears to still be empty!\"\n            log.warning(msg)\n\n            try:\n                webhook = await self.fetch_webhook(constants.Webhooks.dev_log)\n            except discord.HTTPException as e:\n                log.error(f\"Failed to fetch webhook to send empty cache warning: status {e.status}\")\n            else:\n                await webhook.send(f\"<@&{constants.Roles.admin}> {msg}\")\n\n            return\n\n        self._guild_available.set()\n\n    async def on_guild_unavailable(self, guild: discord.Guild) -> None:\n        \"\"\"Clear the internal guild available event when constants.Guild.id becomes unavailable.\"\"\"\n        if guild.id != constants.Guild.id:\n            return\n\n        self._guild_available.clear()\n\n    async def wait_until_guild_available(self) -> None:\n        \"\"\"\n        Wait until the constants.Guild.id guild is available (and the cache is ready).\n\n        The on_ready event is inadequate because it only waits 2 seconds for a GUILD_CREATE\n        gateway event before giving up and thus not populating the cache for unavailable guilds.\n        \"\"\"\n        await self._guild_available.wait()\n\n    async def on_error(self, event: str, *args, **kwargs) -> None:\n        \"\"\"Log errors raised in event listeners rather than printing them to stderr.\"\"\"\n        self.stats.incr(f\"errors.event.{event}\")\n\n        with push_scope() as scope:\n            scope.set_tag(\"event\", event)\n            scope.set_extra(\"args\", args)\n            scope.set_extra(\"kwargs\", kwargs)\n\n            log.exception(f\"Unhandled exception in {event}.\")\n", "path": "bot/bot.py"}, {"content": "import asyncio\nimport logging\nimport os\nimport sys\nfrom logging import Logger, handlers\nfrom pathlib import Path\n\nimport coloredlogs\n\nTRACE_LEVEL = logging.TRACE = 5\nlogging.addLevelName(TRACE_LEVEL, \"TRACE\")\n\n\ndef monkeypatch_trace(self: logging.Logger, msg: str, *args, **kwargs) -> None:\n    \"\"\"\n    Log 'msg % args' with severity 'TRACE'.\n\n    To pass exception information, use the keyword argument exc_info with\n    a true value, e.g.\n\n    logger.trace(\"Houston, we have an %s\", \"interesting problem\", exc_info=1)\n    \"\"\"\n    if self.isEnabledFor(TRACE_LEVEL):\n        self._log(TRACE_LEVEL, msg, args, **kwargs)\n\n\nLogger.trace = monkeypatch_trace\n\nDEBUG_MODE = 'local' in os.environ.get(\"SITE_URL\", \"local\")\n\nlog_level = TRACE_LEVEL if DEBUG_MODE else logging.INFO\nformat_string = \"%(asctime)s | %(name)s | %(levelname)s | %(message)s\"\nlog_format = logging.Formatter(format_string)\n\nlog_file = Path(\"logs\", \"bot.log\")\nlog_file.parent.mkdir(exist_ok=True)\nfile_handler = handlers.RotatingFileHandler(log_file, maxBytes=5242880, backupCount=7, encoding=\"utf8\")\nfile_handler.setFormatter(log_format)\n\nroot_log = logging.getLogger()\nroot_log.setLevel(log_level)\nroot_log.addHandler(file_handler)\n\nif \"COLOREDLOGS_LEVEL_STYLES\" not in os.environ:\n    coloredlogs.DEFAULT_LEVEL_STYLES = {\n        **coloredlogs.DEFAULT_LEVEL_STYLES,\n        \"trace\": {\"color\": 246},\n        \"critical\": {\"background\": \"red\"},\n        \"debug\": coloredlogs.DEFAULT_LEVEL_STYLES[\"info\"]\n    }\n\nif \"COLOREDLOGS_LOG_FORMAT\" not in os.environ:\n    coloredlogs.DEFAULT_LOG_FORMAT = format_string\n\nif \"COLOREDLOGS_LOG_LEVEL\" not in os.environ:\n    coloredlogs.DEFAULT_LOG_LEVEL = log_level\n\ncoloredlogs.install(logger=root_log, stream=sys.stdout)\n\nlogging.getLogger(\"discord\").setLevel(logging.WARNING)\nlogging.getLogger(\"websockets\").setLevel(logging.WARNING)\nlogging.getLogger(__name__)\n\n\n# On Windows, the selector event loop is required for aiodns.\nif os.name == \"nt\":\n    asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())\n", "path": "bot/__init__.py"}]}