diff options
| -rw-r--r-- | botcore/__init__.py | 3 | ||||
| -rw-r--r-- | botcore/async_stats.py | 54 | ||||
| -rw-r--r-- | docs/_static/statsd_additional_objects.inv | bin | 0 -> 308 bytes | |||
| -rw-r--r-- | docs/conf.py | 1 | 
4 files changed, 57 insertions, 1 deletions
| diff --git a/botcore/__init__.py b/botcore/__init__.py index 7d3803f3..59081e57 100644 --- a/botcore/__init__.py +++ b/botcore/__init__.py @@ -1,8 +1,9 @@  """Useful utilities and tools for Discord bot development.""" -from botcore import exts, site_api, utils +from botcore import async_stats, exts, site_api, utils  __all__ = [ +    async_stats,      exts,      utils,      site_api, diff --git a/botcore/async_stats.py b/botcore/async_stats.py new file mode 100644 index 00000000..3ef28e1a --- /dev/null +++ b/botcore/async_stats.py @@ -0,0 +1,54 @@ +"""An async transport method for statsd communication.""" + +import asyncio +import socket +from typing import Optional + +from statsd.client.base import StatsClientBase + +from botcore.utils import scheduling + + +class AsyncStatsClient(StatsClientBase): +    """An async implementation of :obj:`statsd.client.base.StatsClientBase` that supports async stat communication.""" + +    def __init__( +        self, +        loop: asyncio.AbstractEventLoop, +        host: str = 'localhost', +        port: int = 8125, +        prefix: str = None +    ): +        """ +        Create a new :obj:`AsyncStatsClient`. + +        Args: +            loop (asyncio.AbstractEventLoop): The event loop to use when creating the +                :obj:`asyncio.loop.create_datagram_endpoint`. +            host: The host to connect to. +            port: The port to connect to. +            prefix: The prefix to use for all stats. +        """ +        _, _, _, _, addr = socket.getaddrinfo( +            host, port, socket.AF_INET, socket.SOCK_DGRAM +        )[0] +        self._addr = addr +        self._prefix = prefix +        self._loop = loop +        self._transport: Optional[asyncio.DatagramTransport] = None + +    async def create_socket(self) -> None: +        """Use :obj:`asyncio.loop.create_datagram_endpoint` from the loop given on init to create a socket.""" +        self._transport, _ = await self._loop.create_datagram_endpoint( +            asyncio.DatagramProtocol, +            family=socket.AF_INET, +            remote_addr=self._addr +        ) + +    def _send(self, data: str) -> None: +        """Start an async task to send data to statsd.""" +        scheduling.create_task(self._async_send(data), event_loop=self._loop) + +    async def _async_send(self, data: str) -> None: +        """Send data to the statsd server using the async transport.""" +        self._transport.sendto(data.encode('ascii'), self._addr) diff --git a/docs/_static/statsd_additional_objects.inv b/docs/_static/statsd_additional_objects.invBinary files differ new file mode 100644 index 00000000..f84ab9f1 --- /dev/null +++ b/docs/_static/statsd_additional_objects.inv diff --git a/docs/conf.py b/docs/conf.py index 855a5856..47c788df 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -132,6 +132,7 @@ intersphinx_mapping = {      "python": ("https://docs.python.org/3", None),      "discord": ("https://discordpy.readthedocs.io/en/master/", None),      "aiohttp": ("https://docs.aiohttp.org/en/stable/", None), +    "statsd": ("https://statsd.readthedocs.io/en/v3.3/", ("_static/statsd_additional_objects.inv", None)),  } | 
