def test_unregister_url_callback_no_memory(tmpconfig): """Test unregister_url_callback behavior when bot.memory empty""" test_pattern = r'https://(www\.)?example\.com' def url_handler(*args, **kwargs): return None sopel = bot.Sopel(tmpconfig, daemon=False) sopel.unregister_url_callback(test_pattern, url_handler)
def run(config, pid_file, daemon=False): import sopel.bot as bot import sopel.logger from sopel.tools import stderr delay = 20 # Inject ca_certs from config to web for SSL validation of web requests if not config.core.ca_certs: stderr('Could not open CA certificates file. SSL will not ' 'work properly.') def signal_handler(sig, frame): if sig == signal.SIGUSR1 or sig == signal.SIGTERM or sig == signal.SIGINT: stderr('Got quit signal, shutting down.') p.quit('Closing') # Define empty variable `p` for bot p = None while True: if p and p.hasquit: # Check if `hasquit` was set for bot during disconnected phase break try: p = bot.Sopel(config, daemon=daemon) if hasattr(signal, 'SIGUSR1'): signal.signal(signal.SIGUSR1, signal_handler) if hasattr(signal, 'SIGTERM'): signal.signal(signal.SIGTERM, signal_handler) if hasattr(signal, 'SIGINT'): signal.signal(signal.SIGINT, signal_handler) sopel.logger.setup_logging(p) p.run(config.core.host, int(config.core.port)) except KeyboardInterrupt: break except Exception: # TODO: Be specific trace = traceback.format_exc() try: stderr(trace) except Exception: # TODO: Be specific pass logfile = open(os.path.join(config.core.logdir, 'exceptions.log'), 'a') logfile.write('Critical exception in core') logfile.write(trace) logfile.write('----------------------------------------\n\n') logfile.close() os.unlink(pid_file) os._exit(1) if not isinstance(delay, int): break if p.hasquit: break stderr('Warning: Disconnected. Reconnecting in %s seconds...' % delay) time.sleep(delay) os.unlink(pid_file) os._exit(0)
def test_search_url_callbacks(tmpconfig): """Test search_url_callbacks for a registered URL.""" sopel = bot.Sopel(tmpconfig, daemon=False) def url_handler(*args, **kwargs): return None sopel.register_url_callback(r'https://example\.com', url_handler) results = list(sopel.search_url_callbacks('https://example.com')) assert len(results) == 1, 'Expected 1 handler; found %d' % len(results) assert url_handler in results[0], 'Once registered, handler must be found'
def run(settings, pid_file, daemon=False): """Run the bot with these ``settings``. :param settings: settings with which to run the bot :type settings: :class:`sopel.config.Config` :param str pid_file: path to the bot's PID file :param bool daemon: tell if the bot should be run as a daemon """ delay = 20 # Acts as a welcome message, showing the program and platform version at start print_version() # Also show the location of the config file used to load settings print("\nLoaded config file: {}".format(settings.filename)) # Define empty variable `p` for bot p = None while True: if p and p.hasquit: # Check if `hasquit` was set for bot during disconnected phase break try: p = bot.Sopel(settings, daemon=daemon) p.setup() except KeyboardInterrupt: tools.stderr('Bot setup interrupted') break except Exception: # In that case, there is nothing we can do. # If the bot can't setup itself, then it won't run. # This is a critical case scenario, where the user should have # direct access to the exception traceback right in the console. # Besides, we can't know if logging has been set up or not, so # we can't rely on that here. tools.stderr('Unexpected error in bot setup') raise try: p.run(settings.core.host, int(settings.core.port)) except KeyboardInterrupt: break except Exception: err_log = logging.getLogger('sopel.exceptions') err_log.exception('Critical exception in core') err_log.error('----------------------------------------') return ERR_CODE if p.wantsrestart: return -1 if p.hasquit: return 0 LOGGER.warning('Disconnected. Reconnecting in %s seconds...', delay) time.sleep(delay)
def test_reload_plugin_unregistered_plugin(tmpconfig): sopel = bot.Sopel(tmpconfig, daemon=False) sopel.scheduler.stop() sopel.scheduler.join(timeout=10) plugin = sopel._plugins.get('coretasks') assert plugin is not None, 'coretasks should always be loaded' # Unregister the plugin plugin.unregister(sopel) # And now it must raise an exception with pytest.raises(plugins.exceptions.PluginNotRegistered): sopel.reload_plugin(plugin.name)
def test_search_url_callbacks_not_found(tmpconfig): """Test search_url_callbacks when pattern does not match.""" sopel = bot.Sopel(tmpconfig, daemon=False) results = sopel.search_url_callbacks('https://example.com') assert not list(results), 'No handler registered; must return an empty list' def url_handler(*args, **kwargs): return None sopel.register_url_callback(r'https://(www\.)?example\.com', url_handler) results = sopel.search_url_callbacks('https://not-example.com') assert not list(results), 'URL must not match any pattern'
def test_register_unregister_plugin(tmpconfig, mockplugin): sopel = bot.Sopel(tmpconfig, daemon=False) # register the plugin mockplugin.load() mockplugin.register(sopel) assert sopel.has_plugin('mockplugin'), 'The mockplugin must be registered!' # unregister it mockplugin.unregister(sopel) assert not sopel.has_plugin('mockplugin') assert not sopel.rules.has_command('do') assert not sopel.rules.has_nick_command('info') assert not sopel.rules.has_action_command('tell')
def test_reload_plugin_unregistered_plugin(tmpconfig): sopel = bot.Sopel(tmpconfig, daemon=False) # register the plugin handler = plugins.handlers.PyModulePlugin('coretasks', 'sopel') handler.load() handler.register(sopel) # Unregister the plugin handler.unregister(sopel) # And now it must raise an exception with pytest.raises(plugins.exceptions.PluginNotRegistered): sopel.reload_plugin(handler.name)
def test_manual_url_callback_not_found(tmpconfig): """Test that the bot now ignores manually registered URL callbacks.""" # Sopel 8.0 no longer supports `bot.memory['url_callbacks'], and this test # is to make sure that it *really* no longer works. test_pattern = r'https://(www\.)?example\.com' def url_handler(*args, **kwargs): return None sopel = bot.Sopel(tmpconfig, daemon=False) sopel.memory['url_callbacks'] = SopelMemory() # register a callback manually sopel.memory['url_callbacks'][re.compile(test_pattern)] = url_handler results = list(sopel.search_url_callbacks("https://www.example.com")) assert not results, "Manually registered callback must not be found"
def test_search_url_callbacks_compiled_pattern(tmpconfig): """Test search_url_callbacks for a registered compiled regex pattern.""" sopel = bot.Sopel(tmpconfig, daemon=False) url_regex = re.compile(r'https://(www\.)?example\.com') def url_handler(*args, **kwargs): return None sopel.register_url_callback(url_regex, url_handler) results = list(sopel.search_url_callbacks('https://example.com')) assert len(results) == 1, 'Expected 1 handler; found %d' % len(results) assert url_handler in results[0], 'Once registered, handler must be found' results = list(sopel.search_url_callbacks('https://www.example.com')) assert len(results) == 1, 'Regex pattern must match both URLs' assert url_handler in results[0]
def test_register_plugin(tmpconfig, mockplugin): sopel = bot.Sopel(tmpconfig) assert not sopel.has_plugin('mockplugin') mockplugin.load() mockplugin.setup(sopel) mockplugin.register(sopel) assert sopel.has_plugin('mockplugin') assert sopel.rules.has_command('do') assert sopel.rules.has_command('do', plugin='mockplugin') assert sopel.rules.has_nick_command('info') assert sopel.rules.has_nick_command('info', plugin='mockplugin') assert sopel.rules.has_action_command('tell') assert sopel.rules.has_action_command('tell', plugin='mockplugin') assert list(sopel.search_url_callbacks('example.com'))
def test_register_unregister_plugin(tmpconfig): sopel = bot.Sopel(tmpconfig, daemon=False) # since `setup` hasn't been run, there is no registered plugin assert not sopel.has_plugin('coretasks') # register the plugin plugin = plugins.handlers.PyModulePlugin('coretasks', 'sopel') plugin.load() plugin.register(sopel) # and now there is! assert sopel.has_plugin('coretasks') # unregister it plugin.unregister(sopel) assert not sopel.has_plugin('coretasks')
def test_unregister_url_callback_compiled_pattern(tmpconfig): """Test unregister_url_callback works with a compiled regex.""" test_pattern = r'https://(www\.)?example\.com' url_regex = re.compile(test_pattern) def url_handler(*args, **kwargs): return None sopel = bot.Sopel(tmpconfig, daemon=False) # now register a pattern, make sure it still work sopel.register_url_callback(test_pattern, url_handler) assert list(sopel.search_url_callbacks('https://www.example.com')) # unregister using the compiled version sopel.unregister_url_callback(url_regex, url_handler) assert not list(sopel.search_url_callbacks('https://www.example.com'))
def test_unregister_url_callback_unknown_pattern(tmpconfig): """Test unregister_url_callback pass when pattern is unknown.""" test_pattern = r'https://(www\.)?example\.com' def url_handler(*args, **kwargs): return None sopel = bot.Sopel(tmpconfig, daemon=False) # now register a pattern, make sure it still work sopel.register_url_callback(test_pattern, url_handler) assert list(sopel.search_url_callbacks('https://www.example.com')) # unregister another pattern (that doesn't exist) sopel.unregister_url_callback(r'http://localhost', url_handler) # the existing pattern still work assert list(sopel.search_url_callbacks('https://www.example.com'))
def mockbot(configfactory): tmpconfig = configfactory('test.cfg', TMP_CONFIG) url_plugin = plugins.handlers.PyModulePlugin('url', 'sopel.modules') # setup the bot sopel = bot.Sopel(tmpconfig) url_plugin.load() url_plugin.setup(sopel) url_plugin.register(sopel) @plugin.url(re.escape('https://example.com/') + r'(.+)') @plugin.label('handle_urls_https') def url_callback_https(bot, trigger, match): pass @plugin.url(re.escape('http://example.com/') + r'(.+)') @plugin.label('handle_urls_http') def url_callback_http(bot, trigger, match): pass # prepare callables to be registered callables = [ url_callback_https, url_callback_http, ] # clean callables and set plugin name by hand # since the loader and plugin handlers are excluded here for handler in callables: loader.clean_callable(handler, tmpconfig) handler.plugin_name = 'testplugin' # register callables sopel.register_urls(callables) # manually register URL Callback pattern = re.escape('https://help.example.com/') + r'(.+)' def callback(bot, trigger, match): pass sopel.register_url_callback(pattern, callback) return sopel
def test_unregister_url_callback(tmpconfig): """Test unregister_url_callback removes URL callback for a pattern.""" test_pattern = r'https://(www\.)?example\.com' def url_handler(*args, **kwargs): return None sopel = bot.Sopel(tmpconfig, daemon=False) # now register a pattern, make sure it still work sopel.register_url_callback(test_pattern, url_handler) assert list(sopel.search_url_callbacks('https://www.example.com')) # unregister this pattern sopel.unregister_url_callback(test_pattern, url_handler) # now it is not possible to find a callback for this pattern results = list(sopel.search_url_callbacks('https://www.example.com')) assert not results, 'Unregistered URL callback must not work anymore'
def test_register_urls(tmpconfig): sopel = bot.Sopel(tmpconfig) @module.url(r'https://(\S+)/(.+)?') @plugin.label('handle_urls_https') def url_callback_https(bot, trigger, match): pass @module.url(r'http://(\S+)/(.+)?') @plugin.label('handle_urls_http') def url_callback_http(bot, trigger, match): pass # prepare callables to be registered callables = [ url_callback_https, url_callback_http, ] # clean callables and set plugin name by hand # since the loader and plugin handlers are excluded here for handler in callables: loader.clean_callable(handler, tmpconfig) handler.plugin_name = 'testplugin' # register callables sopel.register_urls(callables) # trigger URL callback "handle_urls_https" line = ':[email protected] PRIVMSG #sopel :https://example.com/test' pretrigger = trigger.PreTrigger(sopel.nick, line) matches = sopel.rules.get_triggered_rules(sopel, pretrigger) assert len(matches) == 1 assert matches[0][0].get_rule_label() == 'handle_urls_https' # trigger URL callback "handle_urls_https" line = ':[email protected] PRIVMSG #sopel :http://example.com/test' pretrigger = trigger.PreTrigger(sopel.nick, line) matches = sopel.rules.get_triggered_rules(sopel, pretrigger) assert len(matches) == 1 assert matches[0][0].get_rule_label() == 'handle_urls_http'
def test_unregister_url_callback_manual(tmpconfig): """Test unregister_url_callback removes a specific callback that was added manually""" test_pattern = r'https://(www\.)?example\.com' def url_handler(*args, **kwargs): return None sopel = bot.Sopel(tmpconfig, daemon=False) sopel.memory["url_callbacks"] = SopelMemory() # register a callback manually sopel.memory["url_callbacks"][re.compile(test_pattern)] = url_handler results = list(sopel.search_url_callbacks("https://www.example.com")) assert results[0][0] == url_handler, "Callback must be present" # unregister it sopel.unregister_url_callback(test_pattern, url_handler) results = list(sopel.search_url_callbacks("https://www.example.com")) assert not results, "Callback should have been removed"
def test_register_url_callback_multiple(tmpconfig): """Test register_url_callback replace URL callbacks for a pattern.""" test_pattern = r'https://(www\.)?example\.com' def url_handler(*args, **kwargs): return None def url_handler_replacement(*args, **kwargs): return None sopel = bot.Sopel(tmpconfig, daemon=False) sopel.register_url_callback(test_pattern, url_handler) results = list(sopel.search_url_callbacks('https://www.example.com')) assert url_handler in results[0] sopel.register_url_callback(test_pattern, url_handler_replacement) results = list(sopel.search_url_callbacks('https://www.example.com')) assert len(results) == 1, 'There must be one and only one callback' assert url_handler_replacement in results[0], ( 'Handler must have been replaced')
def __call__(self, settings): obj = bot.Sopel(settings, daemon=False) obj.backend = MockIRCBackend(obj) return obj
def run(settings, pid_file, daemon=False): delay = 20 if not settings.core.ca_certs: tools.stderr( 'Could not open CA certificates file. SSL will not work properly!') def signal_handler(sig, frame): if sig == signal.SIGUSR1 or sig == signal.SIGTERM or sig == signal.SIGINT: tools.stderr('Got quit signal, shutting down.') p.quit('Closing') elif sig == signal.SIGUSR2 or sig == signal.SIGILL: tools.stderr('Got restart signal.') p.restart('Restarting') # Define empty variable `p` for bot p = None while True: if p and p.hasquit: # Check if `hasquit` was set for bot during disconnected phase break try: p = bot.Sopel(settings, daemon=daemon) if hasattr(signal, 'SIGUSR1'): signal.signal(signal.SIGUSR1, signal_handler) if hasattr(signal, 'SIGTERM'): signal.signal(signal.SIGTERM, signal_handler) if hasattr(signal, 'SIGINT'): signal.signal(signal.SIGINT, signal_handler) if hasattr(signal, 'SIGUSR2'): signal.signal(signal.SIGUSR2, signal_handler) if hasattr(signal, 'SIGILL'): signal.signal(signal.SIGILL, signal_handler) p.setup() p.run(settings.core.host, int(settings.core.port)) except KeyboardInterrupt: break except Exception: # TODO: Be specific trace = traceback.format_exc() try: tools.stderr(trace) except Exception: # TODO: Be specific pass logfile = open(os.path.join(settings.core.logdir, settings.basename + '.exceptions.log'), 'a') logfile.write('Critical exception in core') logfile.write(trace) logfile.write('----------------------------------------\n\n') logfile.close() # TODO: This should be handled by command_start # All we should need here is a return value, but replacing the # os._exit() call below (at the end) broke ^C. # This one is much harder to test, so until that one's sorted it # isn't worth the risk of trying to remove this one. os.unlink(pid_file) os._exit(1) if not isinstance(delay, int): break if p.wantsrestart: return -1 if p.hasquit: break tools.stderr( 'Warning: Disconnected. Reconnecting in %s seconds...' % delay) time.sleep(delay) # TODO: This should be handled by command_start # All we should need here is a return value, but making this # a return makes Sopel hang on ^C after it says "Closed!" os.unlink(pid_file) os._exit(0)
def test_register_callables(tmpconfig): sopel = bot.Sopel(tmpconfig) @module.rule(r'(hi|hello|hey|sup)') def rule_hello(bot, trigger): pass @plugin.find(r'(hi|hello|hey|sup)') def rule_find_hello(bot, trigger): pass @plugin.search(r'(hi|hello|hey|sup)') def rule_search_hello(bot, trigger): pass @module.commands('do') @module.example('.do nothing') def command_do(bot, trigger): """The do command does nothing.""" pass @module.commands('main sub') @module.example('.main sub') def command_main_sub(bot, trigger): """A command with subcommand sub.""" pass @module.commands('main other') @module.example('.main other') def command_main_other(bot, trigger): """A command with subcommand other.""" pass @module.nickname_commands('info') @module.example('$nickname: info about this') def nick_command_info(bot, trigger): """Ask Sopel to get some info about nothing.""" pass @module.action_commands('tell') def action_command_tell(bot, trigger): pass @module.commands('mixed') @module.rule('mixing') def mixed_rule_command(bot, trigger): pass @module.event('JOIN') @plugin.label('handle_join_event') def on_join(bot, trigger): pass # prepare callables to be registered callables = [ rule_hello, rule_find_hello, rule_search_hello, command_do, command_main_sub, command_main_other, nick_command_info, action_command_tell, mixed_rule_command, on_join, ] # clean callables and set plugin name by hand # since the loader and plugin handlers are excluded here for handler in callables: loader.clean_callable(handler, tmpconfig) handler.plugin_name = 'testplugin' # register callables sopel.register_callables(callables) # trigger rule "hello" line = ':[email protected] PRIVMSG #sopel :hello' pretrigger = trigger.PreTrigger(sopel.nick, line) matches = sopel.rules.get_triggered_rules(sopel, pretrigger) assert len(matches) == 3 assert matches[0][0].get_rule_label() == 'rule_hello' assert matches[1][0].get_rule_label() == 'rule_find_hello' assert matches[2][0].get_rule_label() == 'rule_search_hello' # trigger command "do" line = ':[email protected] PRIVMSG #sopel :.do' pretrigger = trigger.PreTrigger(sopel.nick, line) matches = sopel.rules.get_triggered_rules(sopel, pretrigger) assert len(matches) == 1 assert matches[0][0].get_rule_label() == 'do' # trigger command with subcommand "main-sub" line = ':[email protected] PRIVMSG #sopel :.main sub' pretrigger = trigger.PreTrigger(sopel.nick, line) matches = sopel.rules.get_triggered_rules(sopel, pretrigger) assert len(matches) == 1 assert matches[0][0].get_rule_label() == 'main-sub' # trigger command with the other subcommand "main-other" line = ':[email protected] PRIVMSG #sopel :.main other' pretrigger = trigger.PreTrigger(sopel.nick, line) matches = sopel.rules.get_triggered_rules(sopel, pretrigger) assert len(matches) == 1 assert matches[0][0].get_rule_label() == 'main-other' # trigger nick command "info" line = ':[email protected] PRIVMSG #sopel :TestBot: info' pretrigger = trigger.PreTrigger(sopel.nick, line) matches = sopel.rules.get_triggered_rules(sopel, pretrigger) assert len(matches) == 1 assert matches[0][0].get_rule_label() == 'info' # trigger action command "tell" line = ':[email protected] PRIVMSG #sopel :\x01ACTION tell\x01' pretrigger = trigger.PreTrigger(sopel.nick, line) matches = sopel.rules.get_triggered_rules(sopel, pretrigger) assert len(matches) == 1 assert matches[0][0].get_rule_label() == 'tell' # trigger rules with event line = ':[email protected] JOIN #Sopel' pretrigger = trigger.PreTrigger(sopel.nick, line) matches = sopel.rules.get_triggered_rules(sopel, pretrigger) assert len(matches) == 1 assert matches[0][0].get_rule_label() == 'handle_join_event' # trigger command "mixed" line = ':[email protected] PRIVMSG #sopel :.mixed' pretrigger = trigger.PreTrigger(sopel.nick, line) matches = sopel.rules.get_triggered_rules(sopel, pretrigger) assert len(matches) == 1 assert matches[0][0].get_rule_label() == 'mixed' # trigger rule "mixed_rule_command" line = ':[email protected] PRIVMSG #sopel :mixing' pretrigger = trigger.PreTrigger(sopel.nick, line) matches = sopel.rules.get_triggered_rules(sopel, pretrigger) assert len(matches) == 1 assert matches[0][0].get_rule_label() == 'mixed_rule_command' # check documentation assert sopel.command_groups == { 'testplugin': ['do', 'info', 'main other', 'main sub', 'mixed'], } assert sopel.doc == { 'do': ( ['The do command does nothing.'], ['.do nothing'], ), 'info': ( ['Ask Sopel to get some info about nothing.'], ['TestBot: info about this'], ), 'main sub': ( ['A command with subcommand sub.'], ['.main sub'], ), 'main other': ( ['A command with subcommand other.'], ['.main other'], ), 'mixed': ( [], [], ) }
def run(settings, pid_file, daemon=False): delay = 20 # Acts as a welcome message, showing the program and platform version at start print_version() if not settings.core.ca_certs: tools.stderr( 'Could not open CA certificates file. SSL will not work properly!') def signal_handler(sig, frame): if sig == signal.SIGUSR1 or sig == signal.SIGTERM or sig == signal.SIGINT: LOGGER.warning('Got quit signal, shutting down.') p.quit('Closing') elif sig == signal.SIGUSR2 or sig == signal.SIGILL: LOGGER.warning('Got restart signal, shutting down and restarting.') p.restart('Restarting') # Define empty variable `p` for bot p = None while True: if p and p.hasquit: # Check if `hasquit` was set for bot during disconnected phase break try: p = bot.Sopel(settings, daemon=daemon) if hasattr(signal, 'SIGUSR1'): signal.signal(signal.SIGUSR1, signal_handler) if hasattr(signal, 'SIGTERM'): signal.signal(signal.SIGTERM, signal_handler) if hasattr(signal, 'SIGINT'): signal.signal(signal.SIGINT, signal_handler) if hasattr(signal, 'SIGUSR2'): signal.signal(signal.SIGUSR2, signal_handler) if hasattr(signal, 'SIGILL'): signal.signal(signal.SIGILL, signal_handler) p.setup() except KeyboardInterrupt: break except Exception: # In that case, there is nothing we can do. # If the bot can't setup itself, then it won't run. # This is a critical case scenario, where the user should have # direct access to the exception traceback right in the console. # Besides, we can't know if logging has been set up or not, so # we can't rely on that here. tools.stderr('Unexpected error in bot setup') raise try: p.run(settings.core.host, int(settings.core.port)) except KeyboardInterrupt: break except Exception: err_log = logging.getLogger('sopel.exceptions') err_log.exception('Critical exception in core') err_log.error('----------------------------------------') # TODO: This should be handled by command_start # All we should need here is a return value, but replacing the # os._exit() call below (at the end) broke ^C. # This one is much harder to test, so until that one's sorted it # isn't worth the risk of trying to remove this one. os.unlink(pid_file) os._exit(1) if not isinstance(delay, int): break if p.wantsrestart: return -1 if p.hasquit: break LOGGER.warning('Disconnected. Reconnecting in %s seconds...', delay) time.sleep(delay) # TODO: This should be handled by command_start # All we should need here is a return value, but making this # a return makes Sopel hang on ^C after it says "Closed!" os.unlink(pid_file) os._exit(0)
def test_remove_plugin_unknown_plugin(tmpconfig): sopel = bot.Sopel(tmpconfig, daemon=False) handler = plugins.handlers.PyModulePlugin('admin', 'sopel.modules') with pytest.raises(plugins.exceptions.PluginNotRegistered): sopel.remove_plugin(handler, [], [], [], [])
def run(settings, pid_file, daemon=False): """Run the bot with these ``settings``. :param settings: settings with which to run the bot :type settings: :class:`sopel.config.Config` :param str pid_file: path to the bot's PID file :param bool daemon: tell if the bot should be run as a daemon """ delay = 20 # Acts as a welcome message, showing the program and platform version at start print_version() # Also show the location of the config file used to load settings print("\nLoaded config file: {}".format(settings.filename)) if not settings.core.ca_certs: tools.stderr( 'Could not open CA certificates file. SSL will not work properly!') # Define empty variable `p` for bot p = None while True: if p and p.hasquit: # Check if `hasquit` was set for bot during disconnected phase break try: p = bot.Sopel(settings, daemon=daemon) p.setup() p.set_signal_handlers() except KeyboardInterrupt: tools.stderr('Bot setup interrupted') break except Exception: # In that case, there is nothing we can do. # If the bot can't setup itself, then it won't run. # This is a critical case scenario, where the user should have # direct access to the exception traceback right in the console. # Besides, we can't know if logging has been set up or not, so # we can't rely on that here. tools.stderr('Unexpected error in bot setup') raise try: p.run(settings.core.host, int(settings.core.port)) except KeyboardInterrupt: break except Exception: err_log = logging.getLogger('sopel.exceptions') err_log.exception('Critical exception in core') err_log.error('----------------------------------------') # TODO: This should be handled by command_start # All we should need here is a return value, but replacing the # os._exit() call below (at the end) broke ^C. # This one is much harder to test, so until that one's sorted it # isn't worth the risk of trying to remove this one. os.unlink(pid_file) os._exit(1) if not isinstance(delay, int): break if p.wantsrestart: return -1 if p.hasquit: break LOGGER.warning('Disconnected. Reconnecting in %s seconds...', delay) time.sleep(delay) # TODO: This should be handled by command_start # All we should need here is a return value, but making this # a return makes Sopel hang on ^C after it says "Closed!" os.unlink(pid_file) os._exit(0)
def run(config, pid_file, daemon=False): import sopel.bot as bot import sopel.logger from sopel.tools import stderr delay = 20 # Inject ca_certs from config to web for SSL validation of web requests if not config.core.ca_certs: stderr('Could not open CA certificates file. SSL will not ' 'work properly.') def signal_handler(sig, frame): if sig == signal.SIGUSR1 or sig == signal.SIGTERM or sig == signal.SIGINT: stderr('Got quit signal, shutting down.') p.quit('Closing') elif sig == signal.SIGUSR2 or sig == signal.SIGILL: stderr('Got restart signal.') p.restart('Restarting') while True: try: p = bot.Sopel(config, daemon=daemon) if hasattr(signal, 'SIGUSR1'): signal.signal(signal.SIGUSR1, signal_handler) if hasattr(signal, 'SIGTERM'): signal.signal(signal.SIGTERM, signal_handler) if hasattr(signal, 'SIGINT'): signal.signal(signal.SIGINT, signal_handler) if hasattr(signal, 'SIGUSR2'): signal.signal(signal.SIGUSR2, signal_handler) if hasattr(signal, 'SIGILL'): signal.signal(signal.SIGILL, signal_handler) sopel.logger.setup_logging(p) p.run(config.core.host, int(config.core.port)) except KeyboardInterrupt: break except Exception: # TODO: Be specific trace = traceback.format_exc() try: stderr(trace) except Exception: # TODO: Be specific pass logfile = open(os.path.join(config.core.logdir, 'exceptions.log'), 'a') logfile.write('Critical exception in core') logfile.write(trace) logfile.write('----------------------------------------\n\n') logfile.close() # TODO: This should be handled in run_script # All we should need here is a return value, but replacing the # os._exit() call below (at the end) broke ^C. # This one is much harder to test, so until that one's sorted it # isn't worth the risk of trying to remove this one. os.unlink(pid_file) os._exit(1) if not isinstance(delay, int): break if p.wantsrestart: return -1 if p.hasquit: break stderr('Warning: Disconnected. Reconnecting in %s seconds...' % delay) time.sleep(delay) # TODO: This should be handled in run_script # All we should need here is a return value, but making this # a return makes Sopel hang on ^C after it says "Closed!" os.unlink(pid_file) os._exit(0)
def mockbot(tmpconfig): obj = bot.Sopel(tmpconfig, daemon=False) obj.backend = MockIRCBackend(obj) return obj