1# -*- coding: utf-8 -*- 2# pylint: disable=missing-docstring 3# pylint: disable=no-method-argument 4# pylint: disable=no-self-use 5# pylint: disable=too-many-public-methods 6# pylint: disable=unused-argument 7# pylint: disable=too-many-instance-attributes 8# pylint: disable=invalid-name 9# pylint: disable=too-many-arguments 10 11from __future__ import unicode_literals 12 13import collections 14import copy 15import io 16import os 17import shutil 18import tarfile 19import types 20 21class MockWeechat(types.ModuleType): 22 23 WEECHAT_RC_ERROR = None 24 WEECHAT_RC_OK = None 25 26 def __init__(self, weechat_dir): 27 self.weechat_dir = weechat_dir 28 29 self.config_options = {} 30 self.script_name = None 31 self.printed = {} 32 self.saved_state = None 33 self.weechat_dir_tar = None 34 self.infos = { 35 ('',) : { 36 'weechat_dir': self.weechat_dir, 37 'version_number': 0x01000100, 38 }, 39 ('server',) : { 40 'irc_nick': 'nick', 41 }, 42 ('server,nick',) : { 43 'irc_buffer' : 'server_nick_buffer', 44 }, 45 ('server,nick2',) : { 46 'irc_buffer' : 'server_nick2_buffer', 47 }, 48 ('server,no_window_nick',) : { 49 'irc_buffer' : 'non_private_buffer', 50 }, 51 ('server,STATUSMSG',) : { 52 'irc_server_isupport_value' : '@+', 53 }, 54 ('server,CHANTYPES',) : { 55 'irc_server_isupport_value' : '#', 56 }, 57 } 58 self.buffers = { 59 None : { 60 'localvar_type' : 'private', 61 'localvar_channel' : 'nick', 62 'localvar_server' : 'server', 63 'localvar_nick' : 'me', 64 }, 65 'server_nick_buffer' : { 66 'localvar_type' : 'private', 67 'localvar_channel' : 'nick', 68 'localvar_server' : 'server', 69 'name' : 'server_nick_buffer_name', 70 'plugin' : 'irc', 71 }, 72 'server_nick2_buffer': { 73 'localvar_type' : 'private', 74 'localvar_channel' : 'nick2', 75 'localvar_server' : 'server', 76 'name' : 'server_nick2_buffer_name', 77 'plugin' : 'irc', 78 }, 79 'non_private_buffer' : { 80 'localvar_type' : 'non_private', 81 'name' : 'non_private_buffer_name', 82 'plugin' : 'irc', 83 } 84 } 85 self.config_written = [] 86 self.bar_items_removed = [] 87 self.config_section_free_options_calls = [] 88 self.config_section_free_calls = [] 89 self.config_free_calls = [] 90 self.buffer_new_calls = [] 91 self.buffer_sets = {} 92 self.buffer_search_calls = [] 93 self.buffer_search_returns = [] 94 self.info_hashtables = {} 95 self.info_get_hashtable_raise = None 96 self.infolists = {} 97 self.config_integer_defaults = {} 98 self.commands = [] 99 self.hook_signals = [] 100 self.info_gets = [] 101 self.window_get_pointers = {} 102 103 def save(self): 104 self.snapshot_weechat_dir() 105 self.saved_state = copy.deepcopy(self.__dict__) 106 107 def restore(self): 108 prev_state = copy.deepcopy(self.saved_state) 109 self.__dict__.clear() 110 self.__dict__.update(prev_state) 111 112 self.restore_weechat_dir() 113 114 def snapshot_weechat_dir(self): 115 tar_io = io.BytesIO() 116 # Note that we cannot use "with tarfile.open" due to lack of support 117 # for context manager in Python 2.6 118 tar = tarfile.open(fileobj=tar_io, mode='w') 119 tar.add(self.weechat_dir, '.') 120 tar.close() 121 self.weechat_dir_tar = tar_io.getvalue() 122 123 def restore_weechat_dir(self): 124 shutil.rmtree(self.weechat_dir) 125 tar_io = io.BytesIO(self.weechat_dir_tar) 126 # Note that we cannot use "with tarfile.open" due to lack of support 127 # for context manager in Python 2.6 128 tar = tarfile.open(fileobj=tar_io) 129 tar.extractall(self.weechat_dir) 130 tar.close() 131 132 def bar_item_new(*args): 133 return 'bar item' 134 135 def bar_item_update(*args): 136 pass 137 138 def buffer_get_string(self, buf, string): 139 return self.buffers[buf].get(string) 140 141 def buffer_search(self, plugin, name): 142 self.buffer_search_calls.append((plugin, name)) 143 return self.buffer_search_returns.pop(0) 144 145 def command(self, *args): 146 self.commands.append(args) 147 148 def config_boolean(self, val): 149 if val == 'on': 150 return 1 151 return 0 152 153 def config_get(self, key): 154 return self.config_options.get(key, '') 155 156 def config_new(*args): 157 return 'config file' 158 159 def config_new_option(self, config_file, section, name, *args): 160 parts = [self.script_name] 161 if section is not None: 162 parts.append(section) 163 parts.append(name) 164 default = args[5] 165 full_option_name = '.'.join(parts) 166 167 self.config_options[full_option_name] = default 168 169 def config_new_section(self, config_file, name, *args): 170 return name 171 172 def config_read(*args): 173 pass 174 175 def config_string(self, key): 176 return key 177 178 def current_buffer(*args): 179 pass 180 181 def hook_command(*args): 182 pass 183 184 def hook_completion(*args): 185 pass 186 187 def hook_config(*args): 188 pass 189 190 def hook_modifier(*args): 191 pass 192 193 def hook_signal(self, *args): 194 self.hook_signals.append(args) 195 196 def info_get(self, name, *args): 197 self.info_gets.append((name, args)) 198 199 return self.infos[args].get(name) 200 201 def info_get_hashtable(self, name, args): 202 # pylint: disable=too-many-branches 203 # pylint: disable=too-many-statements 204 # pylint: disable=too-many-nested-blocks 205 if self.info_get_hashtable_raise: 206 # pylint: disable=raising-bad-type 207 raise self.info_get_hashtable_raise 208 if name in self.info_hashtables: 209 return self.info_hashtables[name].pop() 210 if name == 'irc_message_parse': 211 # Python translation of WeeChat's message parsing code. 212 # 213 # https://github.com/weechat/weechat/blob/bd06f0f60f8c3f5ab883df9c 214 # b876fe29715055b3/src/plugins/irc/irc-message.c#L43-210 215 result = { 216 'arguments' : '', 217 'channel' : '', 218 'host' : '', 219 'nick' : '' 220 } 221 222 nick_set = False 223 224 ptr_message = args['message'] 225 226 if ptr_message[0] == ':': 227 pos3 = ptr_message.find('@') 228 pos2 = ptr_message.find('!') 229 pos = ptr_message.find(' ') 230 if pos2 == -1 or (pos != -1 and pos2 > pos): 231 pos2 = pos3 232 if pos2 != -1 and (pos == -1 or pos > pos2): 233 result['nick'] = ptr_message[1:pos2] 234 nick_set = True 235 elif pos != -1: 236 result['nick'] = ptr_message[1:pos] 237 nick_set = True 238 if pos != -1: 239 result['host'] = ptr_message[1:pos] 240 ptr_message = ptr_message[pos:].lstrip() 241 else: 242 result['host'] = ptr_message[1:] 243 ptr_message = '' 244 245 if ptr_message: 246 pos = ptr_message.find(' ') 247 if pos != -1: 248 result['command'] = ptr_message[:pos] 249 pos += 1 250 while ptr_message[pos] == ' ': 251 pos += 1 252 result['arguments'] = ptr_message[pos:] 253 if ptr_message[pos] != ':': 254 if ptr_message[pos] in ('#', '&', '+', '!'): 255 pos2 = ptr_message[pos:].find(' ') 256 if pos2 != -1: 257 result['channel'] = ptr_message[pos:][:pos2] 258 else: 259 result['channel'] = ptr_message[pos:] 260 else: 261 pos2 = ptr_message[pos:].find(' ') 262 if not nick_set: 263 if pos2 != -1: 264 result['nick'] = ptr_message[pos:][:pos2] 265 else: 266 result['nick'] = ptr_message[pos:] 267 if pos2 != -1: 268 pos3 = pos2 269 pos2 += 1 270 while ptr_message[pos2] == ' ': 271 pos2 += 1 272 if ptr_message[pos2] in ('#', '&', '+', '!'): 273 pos4 = ptr_message[pos2:].find(' ') 274 if pos4 != -1: 275 result['channel'] = \ 276 ptr_message[pos2:][:pos4] 277 else: 278 result['channel'] = ptr_message[pos2:] 279 elif not result['channel']: 280 result['channel'] = ptr_message[pos:][:pos3] 281 else: 282 result['command'] = ptr_message 283 284 return result 285 286 def infolist_free(*args): 287 pass 288 289 def infolist_get(self, *args): 290 return collections.deque(self.infolists.get(args, []) + [False]) 291 292 def infolist_next(self, infolist): 293 # The current item in the infolist is the last item. 294 infolist.rotate(-1) 295 return infolist[-1] 296 297 def infolist_integer(self, infolist, key): 298 return infolist[-1]['integer'][key] 299 300 def infolist_pointer(self, infolist, key): 301 return infolist[-1]['pointer'][key] 302 303 def mkdir_home(self, name, mode): 304 os.mkdir(os.path.join(self.weechat_dir, name), mode) 305 306 def string_eval_expression(self, expr, pointers, extra_vars, options): 307 return 'eval({0})'.format(expr) 308 309 def config_color(self, key): 310 return key 311 312 def color(self, name): 313 return '(color {0})'.format(name) 314 315 def prnt(self, buf, message): 316 self.printed.setdefault(buf, []).append(message) 317 318 def register(self, script_name, *args): 319 self.script_name = script_name 320 321 return True 322 323 def set_server_current_nick(self, server, nick): 324 self.infos[(server, )]['irc_nick'] = nick 325 326 def config_integer_default(self, key): 327 return self.config_integer_defaults.get(key) 328 329 def config_write(self, *args): 330 self.config_written.append(args) 331 332 def config_section_free_options(self, *args): 333 self.config_section_free_options_calls.append(args) 334 335 def config_section_free(self, *args): 336 self.config_section_free_calls.append(args) 337 338 def config_free(self, *args): 339 self.config_free_calls.append(args) 340 341 def bar_item_remove(self, *args): 342 self.bar_items_removed.append(args) 343 344 def buffer_new(self, *args): 345 self.buffer_new_calls.append(args) 346 347 return args[0] 348 349 def buffer_set(self, name, key, value): 350 self.buffer_sets.setdefault(name, {}).update({key: value}) 351 352 def window_get_pointer(self, *args): 353 return self.window_get_pointers[args] 354 355 def add_channel_prefix(self, prefix): 356 self.infos[('server,CHANTYPES',)]['irc_server_isupport_value'] += \ 357 prefix 358 359 def server_no_chantypes(self): 360 self.infos[('server,CHANTYPES',)]['irc_server_isupport_value'] = '' 361 self.infos[('server,STATUSMSG',)]['irc_server_isupport_value'] = '' 362