Skip to content
This repository has been archived by the owner on Oct 23, 2019. It is now read-only.

Commit

Permalink
Merge pull request #453 from linuxdaemon/gonzobot+refactor-client-load
Browse files Browse the repository at this point in the history
Switch client loading to use venusian
  • Loading branch information
linuxdaemon authored Jun 29, 2019
2 parents f8cfb59 + 5256610 commit a1b954e
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 25 deletions.
21 changes: 12 additions & 9 deletions cloudbot/bot.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import asyncio
import collections
import gc
import importlib
import logging
import os
import re
Expand All @@ -13,9 +12,11 @@
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker
from venusian import Scanner
from watchdog.observers import Observer

from cloudbot.client import Client, CLIENTS
from cloudbot import clients
from cloudbot.client import Client
from cloudbot.config import Config
from cloudbot.event import Event, CommandEvent, RegexEvent, EventType
from cloudbot.hook import Action
Expand Down Expand Up @@ -110,6 +111,7 @@ def __init__(self, loop=asyncio.get_event_loop()):
self.loop = loop
self.start_time = time.time()
self.running = True
self.clients = {}
# future which will be called when the bot stopsIf you
self.stopped_future = async_util.create_future(self.loop)

Expand Down Expand Up @@ -191,7 +193,10 @@ def run(self):
return restart

def get_client(self, name: str) -> Type[Client]:
return CLIENTS[name]
return self.clients[name]

def register_client(self, name, cls):
self.clients[name] = cls

def create_connections(self):
""" Create a BotConnection for all the networks defined in the config """
Expand All @@ -202,7 +207,8 @@ def create_connections(self):
_type = config.get("type", "irc")

self.connections[name] = self.get_client(_type)(
self, name, nick, config=config, channels=config['channels']
self, _type, name, nick, config=config,
channels=config['channels']
)
logger.debug("[%s] Created connection.", name)

Expand Down Expand Up @@ -286,11 +292,8 @@ def load_clients(self):
"""
Load all clients from the "clients" directory
"""
client_dir = self.base_dir / "cloudbot" / "clients"
for path in client_dir.rglob('*.py'):
rel_path = path.relative_to(self.base_dir)
mod_path = '.'.join(rel_path.parts).rsplit('.', 1)[0]
importlib.import_module(mod_path)
scanner = Scanner(bot=self)
scanner.scan(clients, categories=['cloudbot.client'])

async def process(self, event):
"""
Expand Down
15 changes: 8 additions & 7 deletions cloudbot/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@
import logging
import random

import venusian

from cloudbot.permissions import PermissionManager
from cloudbot.util import async_util

logger = logging.getLogger("cloudbot")

CLIENTS = {}


def client(_type):
def _decorate(cls):
CLIENTS[_type] = cls
cls._type = _type
def callback_cb(context, name, obj):
context.bot.register_client(_type, cls)

venusian.attach(cls, callback_cb, category='cloudbot.client')
return cls

return _decorate
Expand All @@ -41,9 +43,7 @@ class Client:
:type permissions: PermissionManager
"""

_type = None

def __init__(self, bot, name, nick, *, channels=None, config=None):
def __init__(self, bot, _type, name, nick, *, channels=None, config=None):
"""
:type bot: cloudbot.bot.CloudBot
:type name: str
Expand All @@ -55,6 +55,7 @@ def __init__(self, bot, name, nick, *, channels=None, config=None):
self.loop = bot.loop
self.name = name
self.nick = nick
self._type = _type

if channels is None:
self.channels = []
Expand Down
4 changes: 2 additions & 2 deletions cloudbot/clients/irc.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,15 @@ class IrcClient(Client):
:type _ignore_cert_errors: bool
"""

def __init__(self, bot, name, nick, *, channels=None, config=None):
def __init__(self, bot, _type, name, nick, *, channels=None, config=None):
"""
:type bot: cloudbot.bot.CloudBot
:type name: str
:type nick: str
:type channels: list[str]
:type config: dict[str, unknown]
"""
super().__init__(bot, name, nick, channels=channels, config=config)
super().__init__(bot, _type, name, nick, channels=channels, config=config)

self.use_ssl = config['connection'].get('ssl', False)
self._ignore_cert_errors = config['connection'].get('ignore_cert', False)
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ pyenchant==2.0.0
requests==2.22.0
SQLAlchemy==1.3.5
tweepy==3.7.0
venusian==1.2.0
watchdog==0.9.0
yarl==1.3.0
34 changes: 31 additions & 3 deletions tests/core_tests/test_bot.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import textwrap

import pytest
from mock import patch

from cloudbot import config


@pytest.mark.parametrize('text,result', (
('connection', 'connection'),
('c onn ection', 'c_onn_ection'),
('c+onn ection', 'conn_ection'),
('connection', 'connection'),
('c onn ection', 'c_onn_ection'),
('c+onn ection', 'conn_ection'),
))
def test_clean_name(text, result):
from cloudbot.bot import clean_name
Expand Down Expand Up @@ -36,3 +39,28 @@ def test_get_cmd_regex():
(?:$|\s+)
(?P<text>.*) # Text
""")


class MockConfig(config.Config):
def load_config(self):
self.update({
'connections': [
{
'type': 'irc',
'name': 'foobar',
'nick': 'TestBot',
'channels': [],
'connection': {
'server': 'irc.example.com'
}
}
]
})


def test_load_clients():
with patch('cloudbot.bot.Config', new=MockConfig):
from cloudbot.bot import CloudBot
bot = CloudBot()
assert bot.connections['foobar'].nick == 'TestBot'
assert bot.connections['foobar'].type == 'irc'
5 changes: 2 additions & 3 deletions tests/core_tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@ class Bot(MagicMock):


class TestClient(Client): # pylint: disable=abstract-method
_type = 'TestClient'
_connected = False

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def __init__(self, bot, *args, **kwargs):
super().__init__(bot, 'TestClient', *args, **kwargs)
self.active = True

@property
Expand Down
3 changes: 2 additions & 1 deletion tests/plugin_tests/test_core_connect.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ class MockBot:

def test_core_connects():
bot = MockBot()
client = MockClient(bot, 'foo', 'FooBot', config={
client = MockClient(bot, 'mock', 'foo', 'FooBot', config={
'connection': {
'server': 'example.com',
'password': 'foobar123'
}
})
assert client.type == 'mock'

client.connect()

Expand Down

0 comments on commit a1b954e

Please sign in to comment.