Sun, 16 Aug 2015 10:59:22 +0300
Port to Python 3
bt.py | file | annotate | diff | comparison | revisions | |
calc.py | file | annotate | diff | comparison | revisions | |
cobalt.py | file | annotate | diff | comparison | revisions | |
hgpoll.py | file | annotate | diff | comparison | revisions | |
hgrepo.py | file | annotate | diff | comparison | revisions | |
irc.py | file | annotate | diff | comparison | revisions | |
mod_hg.py | file | annotate | diff | comparison | revisions | |
mod_util.py | file | annotate | diff | comparison | revisions | |
rest.py | file | annotate | diff | comparison | revisions |
--- a/bt.py Tue Aug 11 19:12:30 2015 +0300 +++ b/bt.py Sun Aug 16 10:59:22 2015 +0300 @@ -27,6 +27,9 @@ ''' import suds +import suds.xsd +import suds.xsd.doctor +import suds.client import sys import time import re
--- a/calc.py Tue Aug 11 19:12:30 2015 +0300 +++ b/calc.py Sun Aug 16 10:59:22 2015 +0300 @@ -34,23 +34,11 @@ import time import operator import string +import enum from copy import deepcopy epsilon = 1e-10 -# http://stackoverflow.com/a/2182437 -class Enum (set): - def __init__ (self, *args): - super (Enum, self).__init__ (args) - - def __getattr__ (self, name): - if name in self: - return name - raise AttributeError - - def __setattr__ (self, name, value): - raise AttributeError - class Operator (object): def __init__ (self, name, symbol, operands, priority, function): self.name = name @@ -114,7 +102,7 @@ sumValue = 0 for i in range (0, numRolls): - sumValue += int (random.random() * maxValue) + 1 + sumValue += random.randint (1, maxValue) return sumValue @@ -227,25 +215,25 @@ Tokens = ['(', ')'] # Symbol table -SymbolType = Enum ('CONSTANT', 'FUNCTION', 'OPERATOR', 'TOKEN') +SymbolType = enum.Enum ('SymbolType', ('constant', 'function', 'operator', 'token')) SymbolTypes = {} Symbols = [] for name, value in Constants.items(): Symbols.append (name) - SymbolTypes[name] = SymbolType.CONSTANT + SymbolTypes[name] = SymbolType.constant for name, data in Functions.items(): Symbols.append (name) - SymbolTypes[name] = SymbolType.FUNCTION + SymbolTypes[name] = SymbolType.function for op in Operators: if op.symbol not in Symbols: Symbols.append (op.symbol) - SymbolTypes[op.symbol] = SymbolType.OPERATOR + SymbolTypes[op.symbol] = SymbolType.operator for name in Tokens: - SymbolTypes[name] = SymbolType.TOKEN + SymbolTypes[name] = SymbolType.token Symbols += Tokens Symbols = sorted (Symbols, key=lambda x: len (x), reverse=True) @@ -392,7 +380,7 @@ if sym: symtype = SymbolTypes[sym] - if symtype == SymbolType.CONSTANT: + if symtype == SymbolType.constant: tokens.append (Constants[sym]) else: tokens.append (sym)
--- a/cobalt.py Tue Aug 11 19:12:30 2015 +0300 +++ b/cobalt.py Sun Aug 16 10:59:22 2015 +0300 @@ -38,17 +38,21 @@ import hgpoll as HgPoll import bt as Bt import irc as Irc -import rest as Rest +import rest if __name__ != '__main__': raise ImportError ('cobalt may not be imported as a module') +if sys.version_info < (3, 0): + print ('''cobalt requires Python 3 to run''', file=sys.stderr) + quit (1) + g_BotActive = False # # Exception handling # -def handle_exception(excType, excValue, trace): +def handle_exception (excType, excValue, trace): excepterm (traceback.format_exception(excType, excValue, trace)) def excepterm (data): @@ -58,7 +62,7 @@ Irc.broadcast (line) for client in Irc.all_clients: - if len(data) > 0: + if data: client.exceptdie() else: client.quit_irc() @@ -78,12 +82,10 @@ g_BotActive = False try: - uid = os.geteuid() + if os.geteuid() == 0 and input ('Do you seriously want to run cobalt as root? [y/N] ') != 'y': + quit() except: - uid = -1 - - if uid == 0 and raw_input ('Do you seriously want to run cobalt as root? [y/N] ') != 'y': - quit() + pass ConfigFile.init() ModuleCore.init_data() @@ -104,10 +106,9 @@ break else: raise ValueError ('unknown autoconnect entry "%s"' % (aconn)) - - # Start the REST server + if Config.get_node ('rest').get_value ('active', True): - Rest.RESTServer() + rest.RestServer() g_BotActive = True asyncore.loop()
--- a/hgpoll.py Tue Aug 11 19:12:30 2015 +0300 +++ b/hgpoll.py Sun Aug 16 10:59:22 2015 +0300 @@ -76,7 +76,7 @@ try: repo.clone() - # We need to un-alias a few things, they may be aliased on the host machine (e.g. mine) + # We need to un-alias a few things in case they're aliased comms=['log', 'incoming', 'pull', 'commit', 'push', 'outgoing', 'strip', 'transplant'] try: with open (os.path.join (repo.name, '.hg', 'hgrc'), 'a') as fp: @@ -85,7 +85,7 @@ print ('Warning: unable to alter hgrc of %s: %s' % repo.name, e) print ('Cloning done.') except Exception as e: - raise HgProcessError ('Unable to clone %s from %s: %s' % (repo.name, repo.url, e)) + raise HgProcessError ('Unable to clone %s from %s: %s' % (repo.name, repo.clone_url, e)) quit (1) if not repo.is_valid(): @@ -281,7 +281,7 @@ username = Config.find_developer_by_email (commit['email']) committer = username if username else commit['author'] - descriptor = """commit""" if int (random.random() * 100) != 0 else """KERMIT""" + descriptor = """commit""" if random.randrange (100) != 0 else """KERMIT""" if not isMergeFromSandbox: commitMessage = """\003%d%s\003: new %s\0035 %s%s\003 by\0032 %s\003: %s""" % \ @@ -313,11 +313,6 @@ announce_ticket_resolved (match.group (2), commit['node'], db) break - # Encode messages - for messagelist in messages: - for i in range (0, len (messagelist)): - messagelist[i] = messagelist[i].decode ("utf-8", "ignore").encode("ascii", "ignore") - fullMessageLength = len (''.join (messages[2])) if fullMessageLength > 3000:
--- a/hgrepo.py Tue Aug 11 19:12:30 2015 +0300 +++ b/hgrepo.py Sun Aug 16 10:59:22 2015 +0300 @@ -32,30 +32,26 @@ from configfile import Config class HgRepository (object): - def __init__ (self, reponame): - reponame = reponame.lower() + def __init__ (self, name): + name = name.lower() repoconfig = Config.get_node ('hg').get_node ('repos') - repoinfo = repoconfig.get_node (reponame) + repoinfo = repoconfig.get_node (name) if not repoinfo: - raise ValueError ('Unknown repository "%s"' % reponame) + raise ValueError ('Unknown repository "%s"' % name) - self.name = reponame + self.name = name self.published = not bool (repoinfo.get_value ('extrarepo', default=False)) self.url = repoinfo.get_value ('url') self.version = repoinfo.get_value ('version', default='') self.color = int (repoinfo.get_value ('colorcode', default=0)) if not self.url: - raise ValueError ('Repository %s has no url!' % reponame) + raise ValueError ('Repository %s has no url!' % name) def hg (self, *args): output = subprocess.check_output (['hg', '--cwd', self.name] + list (args)) - - if sys.version_info >= (3, 0): - output = output.decode ('utf-8', 'ignore') - - return output + return output.decode ('utf-8', 'ignore') def is_valid (self): return os.path.isdir (os.path.join (self.name, '.hg')) @@ -74,14 +70,17 @@ return result def get_commit_data (self, rev, **kvargs): - if not kvargs: - raise ValueError ('template arguments must be provided') + try: + if not kvargs: + raise ValueError ('template arguments must be provided') - separator = '^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^' - template = self.split_template (kvargs, separator) - data = self.hg ('log', '--limit', '1', '--rev', rev, '--template', template) - data = data.split (separator) - return self.merge_template (data, kvargs) + separator = '^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^' + template = self.split_template (kvargs, separator) + data = self.hg ('log', '--limit', '1', '--rev', rev, '--template', template) + data = data.split (separator) + return self.merge_template (data, kvargs) + except subprocess.CalledProcessError: + raise ValueError ('''couldn't find changeset %s in %s''' % (node, repo.name)) def incoming (self, maxcommits=0, **kvargs): if not kvargs: @@ -105,6 +104,6 @@ except subprocess.CalledProcessError: return [] - def clone(): - print ('Cloning %s...' % repo.name) - subprocess.call (['hg', 'clone', repo.url, repo.name]) \ No newline at end of file + def clone (self): + print ('Cloning %s from %s...' % (self.name, self.url)) + subprocess.call (['hg', 'clone', self.url, self.name]) \ No newline at end of file
--- a/irc.py Tue Aug 11 19:12:30 2015 +0300 +++ b/irc.py Sun Aug 16 10:59:22 2015 +0300 @@ -31,10 +31,11 @@ import socket import sys import re -import modulecore as ModuleCore import traceback import time import datetime + +import modulecore from configfile import Config import bt as Bt import hgpoll as HgPoll @@ -124,10 +125,7 @@ def write (self, utfdata): try: - if sys.version_info < (3, 0): - self.send_buffer.append (utfdata.decode("utf-8","ignore").encode("ascii","ignore")) - else: - self.send_buffer.append (utfdata) + self.send_buffer.append (utfdata) except UnicodeEncodeError: pass @@ -150,30 +148,24 @@ if self.verbose: print ("[%s] [%s] <- %s" % (get_timestamp(), self.name, line)) - if sys.version_info >= (3, 0): - self.send (bytes (line + '\n', 'UTF-8')) - else: - self.send (line + '\n') + self.send (bytes (line + '\n', 'UTF-8')) time.sleep (0.25) self.send_buffer = [] def handle_read (self): lines = self.recv (4096).splitlines() for line in lines: - try: - line = line.decode ('utf-8', 'ignore') - - if sys.version_info < (3, 0): - line = line.encode ('ascii', 'ignore') - except UnicodeDecodeError: - continue + line = line.decode ('utf-8', 'ignore') if self.verbose: print ("[%s] [%s] -> %s" % (get_timestamp(), self.name, line)) if line[:len('PING :')] == ('PING :'): self.write ("PONG :%s" % line[len('PING :'):]) - self.send_all_now() # pings must be responded to immediately! + + # Pings must be responded to immediately, otherwise command/event/etc processing may + # delay the pong sending enough for the bot to be disconnected over a timeout. + self.send_all_now() else: words = line.split(" ") if len(words) >= 2: @@ -238,13 +230,13 @@ return if channel != self.mynick: - ModuleCore.call_hook (bot=self, hookname='chanmsg', channel=channel, sender=sender, + modulecore.call_hook (bot=self, hookname='chanmsg', channel=channel, sender=sender, ident=user, host=host, message=message, replyto=replyto) else: - ModuleCore.call_hook (bot=self, hookname='querymsg', sender=sender, ident=user, + modulecore.call_hook (bot=self, hookname='querymsg', sender=sender, ident=user, host=host, message=message, replyto=replyto) - ModuleCore.call_hook (bot=self, hookname='privmsg', target=channel, sender=sender, + modulecore.call_hook (bot=self, hookname='privmsg', target=channel, sender=sender, ident=user, host=host, message=message, replyto=replyto) Bt.process_message (self, line, replyto) @@ -276,10 +268,10 @@ 'replyto': replyto, 'cmdname': command, 'message': message} try: - ModuleCore.call_command (self, **kvargs) + modulecore.call_command (self, **kvargs) return - except ModuleCore.CommandError as e: - lines = str (e).split ('\n') + except modulecore.CommandError as e: + lines = str (e).splitlines() self.privmsg (replyto, 'error: %s' % lines[0]) for line in lines[1:]:
--- a/mod_hg.py Tue Aug 11 19:12:30 2015 +0300 +++ b/mod_hg.py Sun Aug 16 10:59:22 2015 +0300 @@ -53,13 +53,6 @@ }, { - 'name': 'hg', - 'description': 'Executes a hg command', - 'args': '<command...>', - 'level': 'admin', - }, - - { 'name': 'resolves', 'description': '''Manually cause a ticket to be resolved by a changeset''', 'args': '<ticket> <changeset>', @@ -137,20 +130,16 @@ def cmd_cset (bot, args, reply, **rest): node, repo = resolve_node (args['key']) - - try: - commit = repo.get_commit_data (rev=node, - node='node|short', - message='desc', - author='author', - diffstat='diffstat', - time='date|hgdate', - bookmarks='bookmarks', - latesttagdistance='latesttagdistance', - latesttag='latesttag', - email='author|email') - except Subprocess.CalledProcessError: - command_error ('''couldn't find changeset %s in %s''' % (node, repo.name)) + commit = repo.get_commit_data (rev=node, + node='node|short', + message='desc', + author='author', + diffstat='diffstat', + time='date|hgdate', + bookmarks='bookmarks', + latesttagdistance='latesttagdistance', + latesttag='latesttag', + email='author|email') del node commit['date'] = datetime.utcfromtimestamp (int (commit['time'].split (' ')[0])) @@ -207,12 +196,5 @@ reply ('url: %s/commits/%s' % (repo.url, commit['node'])) -def cmd_hg (bot, args, reply, **rest): - try: - result = subprocess.check_output (['hg'] + args['command']) - reply (result) - except Exception as e: - command_error (str (e)) - def cmd_resolves (bot, args, **rest): hgpoll.announce_ticket_resolved (args['ticket'], args['changeset']) \ No newline at end of file
--- a/mod_util.py Tue Aug 11 19:12:30 2015 +0300 +++ b/mod_util.py Sun Aug 16 10:59:22 2015 +0300 @@ -29,7 +29,6 @@ from __future__ import print_function import math import json -import subprocess import re from modulecore import command_error import modulecore as ModuleCore @@ -210,28 +209,6 @@ def cmd_calc (bot, reply, args, **rest): reply (calc.Calculator().calc (args['expression'])) - # expr = args['expression'] - # try: - # Substitute some mathematical constants - # expr = mathsubstitute (expr, 'pi' , 3.14159265358979323846264338327950288419716939937510) - # expr = mathsubstitute (expr, 'e' , 2.71828182845904523536028747135266249775724709369995) - # expr = mathsubstitute (expr, 'phi', 1.6180339887498948482) # golden ratio - - # result = subprocess.check_output (['calc', '--', expr], stderr=subprocess.STDOUT) \ - # .replace ('\t', '') \ - # .replace ('\n', '') - - # errmatch = re.compile (r'^.*\bError\b.*$').match (result) - - # if errmatch: - # command_error ('math error') - # return - - # reply ('Result: %s' % result) - # except subprocess.CalledProcessError as e: - # command_error (e.output.split('\n')[0]) - # except OSError as e: - # command_error ('failed to execute calc: ' + e.strerror) def cmd_more (commandObject, **rest): ModuleCore.print_responses (commandObject)
--- a/rest.py Tue Aug 11 19:12:30 2015 +0300 +++ b/rest.py Sun Aug 16 10:59:22 2015 +0300 @@ -192,7 +192,7 @@ self.httpdata += data.replace ('\r', '') def finish (self): - handle_rest_http (self.httpdata.split ('\n'), self.address[0]) + handle_rest_http (self.httpdata.splitlines(), self.address[0]) self.close() def handle_write (self): @@ -202,7 +202,7 @@ def handle_error (self): raise -class RESTServer (asyncore.dispatcher): +class RestServer (asyncore.dispatcher): def __init__ (self): global g_portnumber