Port to Python 3

Sun, 16 Aug 2015 10:59:22 +0300

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Sun, 16 Aug 2015 10:59:22 +0300
changeset 152
1b734faab67a
parent 151
e24793fae424
child 153
497b7290977d

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
 

mercurial