From b0589a0f786e5e43fe43061614d681c7354e40ea Mon Sep 17 00:00:00 2001 From: Guillaume Ayoub Date: Fri, 29 Mar 2019 14:42:18 +0100 Subject: [PATCH] Add a connection factory for the async client Related to #146. Using a factory lets users easily handle needed features. This doesn't fully solve #146 at all, but allows for quick workarounds. The default factory accepts random keywords during initialization, that will be given to loop.create_connection. The parameters are different from Factory, but they more or less cover the same features: IPv6 and SSL are now supported out-of-the-box for example. This change shouldn't break any existing code using the library. It can be greatly improved (by improving constistency for example), but it's a start. --- irc/client_aio.py | 17 ++++++++++------- irc/connection.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/irc/client_aio.py b/irc/client_aio.py index 7504ad2..a2dc53e 100644 --- a/irc/client_aio.py +++ b/irc/client_aio.py @@ -43,6 +43,7 @@ import threading import logging +from . import connection from .client import ServerConnection, ServerNotConnectedError, Reactor,\ SimpleIRCClient, Event, _ping_ponger @@ -99,8 +100,8 @@ class AioConnection(ServerConnection): protocol_class = IrcProtocol async def connect( - self, server, port, nickname, - password=None, username=None, ircname=None + self, server, port, nickname, password=None, username=None, + ircname=None, connect_factory=connection.AioFactory() ): """Connect/reconnect to a server. @@ -113,6 +114,9 @@ async def connect( * username - The username * ircname - The IRC name ("realname") + * connect_factory - An async callable that takes the event loop and the + server address, and returns a connection (with a socket interface) + This function can be called to reconnect a closed connection. Returns the AioProtocol instance (used for handling incoming @@ -133,12 +137,11 @@ async def connect( self.username = username or nickname self.ircname = ircname or nickname self.password = password + self.connect_factory = connect_factory - connection = self.reactor.loop.create_connection( - lambda: self.protocol_class(self, self.reactor.loop), - self.server, - self.port, - ) + protocol_instance = self.protocol_class(self, self.reactor.loop) + connection = self.connect_factory( + protocol_instance, self.server_address) transport, protocol = await connection self.transport = transport diff --git a/irc/connection.py b/irc/connection.py index 05b40fb..b32b2c9 100644 --- a/irc/connection.py +++ b/irc/connection.py @@ -49,3 +49,39 @@ def connect(self, server_address): sock.connect(server_address) return sock __call__ = connect + + +class AioFactory: + """ + A class for creating async custom socket connections. + + To create a simple connection: + + server_address = ('localhost', 80) + Factory()(protocol_instance, server_address) + + To create an SSL connection: + + Factory(ssl=True)(protocol_instance, server_address) + + To create an IPv6 connection: + + Factory(ipv6=True)(protocol_instance, server_address) + + Note that Factory doesn't save the state of the socket itself. The + caller must do that, as necessary. As a result, the Factory may be + re-used to create new connections with the same settings. + + """ + + family = socket.AF_INET + + def __init__(self, **kwargs): + self.connection_args = kwargs + + def connect(self, protocol_instance, server_address): + return protocol_instance.loop.create_connection( + lambda: protocol_instance, *server_address, + **self.connection_args) + + __call__ = connect