# HG changeset patch # User Teemu Piippo # Date 1429461942 -10800 # Node ID aeb0d078886901444ec5f33c7f4dee47573577d0 # Parent f899af683bbea5cfc5d81b7db08a951cf1326650 - added commits.db and mercurial support restructure - added new, from-scratch calculator for .calc diff -r f899af683bbe -r aeb0d0788869 .hgignore --- a/.hgignore Sat Apr 11 21:02:54 2015 +0300 +++ b/.hgignore Sun Apr 19 19:45:42 2015 +0300 @@ -1,6 +1,7 @@ syntax:glob cobalt*.json* untracked -commits.txt +commits.db *.pyc zandronum* +cobalt.cfg diff -r f899af683bbe -r aeb0d0788869 bt.py --- a/bt.py Sat Apr 11 21:02:54 2015 +0300 +++ b/bt.py Sun Apr 19 19:45:42 2015 +0300 @@ -100,6 +100,9 @@ global btannounce_timeout global btannounce_id + if not suds_active: + return + if time.time() >= btannounce_timeout: update_checktimeout() newid = btannounce_id diff -r f899af683bbe -r aeb0d0788869 calc.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/calc.py Sun Apr 19 19:45:42 2015 +0300 @@ -0,0 +1,474 @@ +import re +import cmath +import math +import random +import time +import operator +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 + self.symbol = symbol + self.operands = operands + self.priority = priority + self.function = function + + def __str__ (self): + return '''''' % self.name + + def __repr__ (self): + return self.__str__() + +class FunctionCall (object): + def __init__ (self, funcname, args): + assert (type(args) is list) + self.funcname = funcname + self.args = args + + def __str__ (self): + return '''''' % (self.funcname, self.args) + + def __repr__ (self): + return self.__str__() + +def do_realf (func, *args): + for x in args: + if x.imag: + raise ValueError ('%s called with a complex number' % func.__name__) + + return func (*[x.real for x in args]) + +def do_intf (func, *args): + for x in args: + if x.imag: + raise ValueError ('%s called with a complex number' % func.__name__) + + if x.real - math.floor (x.real): + raise ValueError ('%s called with a floating point number' % func.__name__) + + return func (*[int (x.real) for x in args]) + +def realf (func): + return lambda *args: do_realf (func, *args) + +def intf (func): + return lambda *args: do_intf (func, *args) + +Operators = [] +OperatorData = { + 'lneg': { 'symbol': '!', 'operands': 1, 'priority': 5, 'function': lambda x: not x }, + 'compl': { 'symbol': '~', 'operands': 1, 'priority': 5, 'function': intf (operator.inv) }, + 'neg': { 'symbol': '-', 'operands': 1, 'priority': 5, 'function': lambda x: -x }, + 'pow': { 'symbol': '**', 'operands': 2, 'priority': 10, 'function': lambda x, y: x ** y }, + 'mul': { 'symbol': '*', 'operands': 2, 'priority': 50, 'function': lambda x, y: x * y }, + 'div': { 'symbol': '/', 'operands': 2, 'priority': 50, 'function': lambda x, y: x / y }, + 'mod': { 'symbol': '%', 'operands': 2, 'priority': 50, 'function': lambda x, y: math.fmod (x, y) }, + 'add': { 'symbol': '+', 'operands': 2, 'priority': 100, 'function': lambda x, y: x + y }, + 'sub': { 'symbol': '-', 'operands': 2, 'priority': 100, 'function': lambda x, y: x - y }, + 'eq': { 'symbol': '==', 'operands': 2, 'priority': 500, 'function': lambda x, y: x == y }, + 'neq': { 'symbol': '!=', 'operands': 2, 'priority': 500, 'function': lambda x, y: x != y }, + 'lt': { 'symbol': '<', 'operands': 2, 'priority': 500, 'function': lambda x, y: x < y }, + 'lteq': { 'symbol': '<=', 'operands': 2, 'priority': 500, 'function': lambda x, y: x <= y }, + 'gt': { 'symbol': '>', 'operands': 2, 'priority': 500, 'function': lambda x, y: x > y }, + 'gteq': { 'symbol': '>=', 'operands': 2, 'priority': 500, 'function': lambda x, y: x >= y }, + 'btand': { 'symbol': '&', 'operands': 2, 'priority': 600, 'function': intf (operator.and_) }, + 'btxor': { 'symbol': '^', 'operands': 2, 'priority': 601, 'function': intf (operator.xor) }, + 'btor': { 'symbol': '|', 'operands': 2, 'priority': 602, 'function': intf (operator.or_) }, + 'and': { 'symbol': '&&', 'operands': 2, 'priority': 603, 'function': lambda x, y: x and y }, + 'or': { 'symbol': '||', 'operands': 2, 'priority': 604, 'function': lambda x, y: x or y }, +} + +for name, data in OperatorData.iteritems(): + Operators.append (Operator (name=name, symbol=data['symbol'], operands=data['operands'], + priority=data['priority'], function=data['function'])) + +OperatorSymbols={} +for op in Operators: + if op.symbol in OperatorSymbols: + OperatorSymbols[op.symbol].append (op) + else: + OperatorSymbols[op.symbol] = [op] + +Constants = { + 'pi': cmath.pi, + 'e': cmath.e, + 'phi': 1.6180339887498948482, + 'epsilon': epsilon, +} + +random.seed (time.time()) + +Functions = { + 'round': { 'function': realf (round) }, + 'floor': { 'function': realf (math.floor) }, + 'ceil': { 'function': realf (math.ceil) }, + 'fact': { 'function': intf (math.factorial) }, + 'abs': { 'function': realf (math.fabs) }, + 'degrees': { 'function': realf (math.degrees) }, + 'radians': { 'function': realf (math.radians) }, + 'erf': { 'function': realf (math.erf) }, + 'erfc': { 'function': realf (math.erfc) }, + 'gamma': { 'function': realf (math.gamma) }, + 'lgamma': { 'function': realf (math.lgamma) }, + 'sqrt': { 'function': cmath.sqrt }, + 'ln': { 'function': cmath.log }, + 'log': { 'function': cmath.log10 }, + 'sin': { 'function': cmath.sin }, + 'sinh': { 'function': cmath.sinh }, + 'asin': { 'function': cmath.asin }, + 'asinh': { 'function': cmath.asinh }, + 'cos': { 'function': cmath.cos }, + 'cosh': { 'function': cmath.cosh }, + 'acos': { 'function': cmath.acos }, + 'acosh': { 'function': cmath.acosh }, + 'tan': { 'function': cmath.tan }, + 'tanh': { 'function': cmath.tanh }, + 'atan': { 'function': cmath.atan }, + 'atanh': { 'function': cmath.atanh }, + 'exp': { 'function': cmath.exp }, + 'phase': { 'function': cmath.phase }, + 'lg': { 'function': lambda x: cmath.log (x, 2) }, + 're': { 'function': lambda x: x.real }, + 'im': { 'function': lambda x: x.imag }, + 'conjugate':{ 'function': lambda x: x.conjugate() }, + 'rand': { 'function': random.random }, +} + +Tokens = ['(', ')'] + +# Symbol table +SymbolType = Enum ('CONSTANT', 'FUNCTION', 'OPERATOR', 'TOKEN') +SymbolTypes = {} +Symbols = [] + +for name, value in Constants.iteritems(): + Symbols.append (name) + SymbolTypes[name] = SymbolType.CONSTANT + +for name, data in Functions.iteritems(): + Symbols.append (name) + SymbolTypes[name] = SymbolType.FUNCTION + +for op in Operators: + if op.symbol not in Symbols: + Symbols.append (op.symbol) + SymbolTypes[op.symbol] = SymbolType.OPERATOR + +for name in Tokens: + SymbolTypes[name] = SymbolType.TOKEN + +Symbols += Tokens +Symbols = sorted (Symbols, key=lambda x: len (x), reverse=True) + +def parse_number (expr): + """Tries to parse a number from the expression. Returns (value, length) on success.""" + i = 0 + value = None + + if expr[0] == 'i': + # Special case, we just have 'i' -- need special handling here because otherwise this would + # call float('') in the complex number routine, which throws an error. + # TODO: maybe 'i' can be a symbol instead? + return (1j, 1) + + # Try parse increasingly long substrings until we are unable to create a float or complex number + # from it. + try: + while i < len (expr): + if expr[i] == 'i': + value = complex (0.0, float (expr[:i])) + else: + value = complex (float (expr [:i + 1]), 0.0) + i += 1 + + return (value, i) + except ValueError: + if i != 0: + # Got a number (the error happened when we tried to parse past the number) + return (value, i) + else: + # The error happened on the first character. So this is not a number. + return None + +def parse_symbol (expr): + for sym in Symbols: + if expr[:len (sym)] == sym: + return sym + + return None + +def tokenize (expr): + expr = re.sub ('\s+', '', expr.strip()) + i=0 + tokens = [] + + while i < len(expr): + sym = parse_symbol (expr[i:]) + + if sym: + symtype = SymbolTypes[sym] + if symtype == SymbolType.CONSTANT: + tokens.append (Constants[sym]) + else: + tokens.append (sym) + + i += len(sym) + continue + + result = parse_number (expr[i:]) + if result: + num, length = result + tokens.append (num) + i += length + continue + + raise ValueError ("""bad expression, couldn't parse: %s""" % expr[i:]) + + return tokens + +def rindex (li, value): + return (len(li) - 1) - li[::-1].index(value) + +def process_parens (expr): + """Processes parentheses of expr into sublists in-place. + [1.0, '*', '(', 3.5, '+', 1j, ')'] + -> [1.0, '*', [3.5, '+', 1j]]""" + if '(' not in expr and ')' not in expr: + return + + try: + parenStart = rindex (expr, '(') + parenEnd = expr.index (')', parenStart) + except ValueError: + raise ValueError ("""mismatched parentheses in expression: %s""" % expr) + + subexpr = expr[parenStart + 1:parenEnd] + del expr[parenStart + 1:parenEnd + 1] + expr[parenStart] = subexpr + process_parens (expr) + +def process_functions (expr): + """Processes functions in-place""" + i = 0 + while i < len (expr): + if type (expr[i]) is list: + process_functions (expr[i]) + + if (type(expr[i]) is not str) or (expr[i] not in Functions): + i += 1 + continue + + # Ensure that arguments follow + if (i + 1 >= len (expr)) or (type (expr[i + 1]) is not list): + raise ValueError ("""function %s used without arguments""" % expr[i]) + + args = expr[i + 1] + del expr[i + 1] + expr[i] = FunctionCall (expr[i], args) + i += 1 + +def is_operand (x): + # Operands can be either lists (which also mean parens, thus can be single-expressions) + # or complex numbers + return type(x) in [complex, list] + +def find_fitting_operator (sym, numOperands): + # Pass 1: exact numOperands match + for op in Operators: + if op.symbol != sym: + continue + + if op.operands == numOperands: + # print '%s, %d -> %s (1st pass)' % (sym, numOperands, op) + return op + + # Pass 2: by symbol + for op in Operators: + if op.symbol == sym: + # print '%s, %d -> %s (2nd pass)' % (sym, numOperands, op) + return op + + raise ValueError ('''unknown operator %s!''' % sym) + +def process_operators (expr): + """Processes operators""" + i = 0 + + # Find all operators in this expression + while i < len (expr): + if type (expr[i]) is list: + process_functions (expr[i]) + process_operators (expr[i]) + + if type (expr[i]) is FunctionCall: + process_functions (expr[i].args) + process_operators (expr[i].args) + + if (type(expr[i]) is not str) or (expr[i] not in OperatorSymbols): + i += 1 + continue + + args = [] + argIndices = [] + if i > 0 and is_operand (expr[i - 1]): + args.append (expr[i - 1]) + argIndices.append (i - 1) + + if i - 1 < len(expr) and is_operand (expr[i + 1]): + args.append (expr[i + 1]) + argIndices.append (i + 1) + + # Resolve operators with the same symbol based on operand count + numOperands = 0 + for arg in args: + if is_operand (arg): + numOperands += 1 + + expr[i] = find_fitting_operator (expr[i], numOperands) + i += 1 + +def find_priority_operator (expr): + """Finds the operator with most priority in the expression""" + bestOp = None + bestOpIndex = -1 + + for i in range (0, len (expr)): + sym = expr[i] + + if type (sym) is not Operator: + continue + + if not bestOp or sym.priority < bestOp.priority: + bestOp = sym + bestOpIndex = i + + return (bestOp, bestOpIndex) + +def realPrint (x): + print x + +tabs='' +def evaluate (expr, verbose=False): + global tabs + printFunc = realPrint if verbose else lambda x:None + printFunc (tabs + 'Preprocess: %s' % expr) + + # If there are sub-expressions in here, those need to be evaluated first + i = 0 + while i < len (expr): + sym = expr[i] + + if type (sym) is list and sym: + printFunc (tabs + 'Evaluating sub-expression: %s' % (sym)) + tabs += '\t' + expr[i] = evaluate (list (sym), verbose) + tabs = tabs[:-1] + printFunc (tabs + '-> %s' % expr[i]) + + # If there are function calls, evaluate those + if type (sym) is FunctionCall: + tabs += '\t' + if sym.args: + sym.args = [evaluate (sym.args, verbose)] + tabs = tabs[:-1] + + printFunc (tabs + 'Evaluating function call: %s' % (sym)) + expr[i] = Functions[sym.funcname]['function'] (*sym.args) + printFunc (tabs + '-> %s' % expr[i]) + + i += 1 + + printFunc (tabs + 'Evaluate: %s' % expr) + runaway = 0 + while True: + runaway += 1 + if runaway > 1000: + raise ValueError ('infinite loop detected') + + op, i = find_priority_operator (expr) + if not op: + break + + if op.operands == 2: + argIndices = [i - 1, i + 1] + else: + argIndices = [i + 1] + + args = [expr[x] for x in argIndices] + argIndices = sorted (argIndices, reverse=True) + printFunc (tabs + 'Processing: (%s, %d) with args %s' % (op, i, args)) + expr[i] = op.function (*args) + printFunc (tabs + '-> %s' % expr[i]) + + for i2 in argIndices: + del expr[i2] + + printFunc (tabs + 'Result: %s' % expr[0]) + + if len(expr) != 1: + printFunc (tabs + 'Bad expression detected, tokens: %s' % expr) + raise ValueError ('malformed expression') + + return expr[0] + +def repr_number (x): + """Returns a string representation for a real number""" + rep = '%.10f' % x + + if '.' in rep: + while rep[-1] == '0': + rep = rep[:-1] + + if rep[-1] == '.': + rep = rep[:-1] + + return rep + +def repr_imaginary (x): + rep = repr_number (x) + + if rep == '1': + return 'i' + + if rep == '-1': + return '-i' + + return rep + 'i' + +def represent (x): + """Returns a string representation of a float or complex number""" + if math.fabs (x.imag) > epsilon: + if math.fabs (x.real) > epsilon: + # Complex number + return '%s %s %s' % (repr_number (x.real), + '+' if x.imag >= 0 else '-', + repr_imaginary (math.fabs (x.imag))) + else: + # Pure imaginary number + return repr_imaginary (x.imag) + else: + # Real number + return repr_number (x.real) + +def calc (expr, verbose=False): + expr = tokenize (expr) + process_parens (expr) + process_functions (expr) + process_operators (expr) + return represent (evaluate (expr, verbose)) diff -r f899af683bbe -r aeb0d0788869 commitsdb.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/commitsdb.py Sun Apr 19 19:45:42 2015 +0300 @@ -0,0 +1,84 @@ +class CommitsDb (object): + def __init__(self): + needNew = not os.path.isfile ('commits.db') + self.db = sqlite3.connect ('commits.db') + + if needNew: + self.create_new() + + def create_new (self): + self.db.executescript (''' + DROP TABLE IF EXISTS COMMITS; + DROP TABLE IF EXISTS REPOS; + DROP TABLE IF EXISTS REPOCOMMITS; + CREATE TABLE IF NOT EXISTS COMMITS + ( + Node text NOT NULL, + Dateversion text NOT NULL, + PRIMARY KEY (Node) + ); + CREATE TABLE IF NOT EXISTS REPOS + ( + Name text NOT NULL, + PRIMARY KEY (Name) + ); + CREATE TABLE IF NOT EXISTS REPOCOMMITS + ( + Reponame text, + Node text, + FOREIGN KEY (Reponame) REFERENCES REPOS(Name), + FOREIGN KEY (Node) REFERENCES COMMITS(Node) + ); + ''') + + print 'Building commits.db...' + for repo in all_repo_names(): + print 'Adding commits from %s...' % repo + + data = subprocess.check_output (['hg', '--cwd', repo, 'log', '--template', + '{node} {date|hgdate}\n']).splitlines() + + for line in data: + changeset, timestamp, tz = line.split(' ') + self.add_commit (repo, changeset, int (timestamp)) + + self.commit() + + def add_commit (self, repo, changeset, timestamp): + dateversion = datetime.utcfromtimestamp (timestamp).strftime ('%y%m%d-%H%M') + self.db.execute (''' + INSERT OR IGNORE INTO REPOS + VALUES (?) + ''', (repo,)) + + self.db.execute (''' + INSERT OR IGNORE INTO COMMITS + VALUES (?, ?) + ''', (changeset, dateversion)) + + self.db.execute (''' + INSERT INTO REPOCOMMITS + VALUES (?, ?) + ''', (repo, changeset)) + + def get_commit_repos (self, node): + cursor = self.db.execute (''' + SELECT Reponame + FROM REPOCOMMITS + WHERE Node LIKE ? + ''', (node + '%',)) + + results = cursor.fetchall() + return list (set (zip (*results)[0])) if results else [] + + def find_commit_by_dateversion (self, dateversion): + cursor = self.db.execute (''' + SELECT Node + FROM COMMITS + WHERE Dateversion = ? + ''', (dateversion,)) + result = cursor.fetchone() + return result[0] if result else None + + def commit(self): + self.db.commit() \ No newline at end of file diff -r f899af683bbe -r aeb0d0788869 hgpoll.py --- a/hgpoll.py Sat Apr 11 21:02:54 2015 +0300 +++ b/hgpoll.py Sun Apr 19 19:45:42 2015 +0300 @@ -7,7 +7,10 @@ from datetime import datetime from configfile import Config import utility -g_needCommitsTxtRebuild = True +g_CommitsDb = None + +def all_repo_names(): + return Config.get_node ('hg').get_value ('repos', {}).keys() class CommitsDb (object): def __init__(self): @@ -94,8 +97,6 @@ def commit(self): self.db.commit() -g_CommitsDb = CommitsDb() - def color_for_repo (repo_name): repo_name = repo_name.lower() repoconfig = Config.get_node ('hg').get_node ('repos') @@ -106,7 +107,10 @@ return 0 def prettify_bookmarks (bookmarks): - return "\0036 [\002%s\002]" % bookmarks + if bookmarks: + return "\0036 [\002%s\002]" % bookmarks + else: + return '' def get_repo_info (reponame): reponame = reponame.lower() @@ -125,14 +129,20 @@ if not os.path.exists (repo_name): os.makedirs (repo_name) - try: - zanrepo.hg_command ('id', '.') - except hgapi.hgapi.HgException: + if not os.path.isfile (os.path.join (repo_name, '.hg', 'hgrc')): # If the repo does not exist, clone it. repo_url = get_repo_info (repo_name).get_value ('url') try: print ('Cloning %s...' % repo_name) zanrepo.hg_clone (repo_url, repo_name) + + # We need to un-alias a few things, they can be aliased on the host machine (e.g. mine) + comms=['log', 'incoming', 'pull', 'commit', 'push', 'outgoing', 'strip', 'transplant'] + try: + with open (os.path.join (repo_name, '.hg', 'hgrc'), 'a') as fp: + fp.write ('\n[alias]\n' + ''.join(['%s=%s\n' % (x, x) for x in comms])) + except Exception as e: + print ('Warning: unable to alter hgrc of %s: %s' % repo_name, e) print ('Cloning done.') except Exception as e: print ('Unable to clone %s from %s: %s' % (repo_name, repo_url, e)) @@ -250,14 +260,15 @@ Bt.post_note (ticket_id, message) -def all_repo_names(): - return Config.get_node ('hg').get_value ('repos', {}).keys() +def init(): + global repocheck_timeout + global g_CommitsDb -def init(): for repo in all_repo_names(): check_repo_exists (repo) - global repocheck_timeout + repocheck_timeout = time.time() + 15 + g_CommitsDb = CommitsDb() def get_commit_data (repo, rev, template): return repo.hg_command ('log', '-l', '1', '-r', rev, '--template', template) @@ -309,17 +320,18 @@ repo = hgapi.Repo (repo_name) commit_data = [] - delimeter = '@@@@@@@@@@' + delimeter = '^^^^^^^^^^' + delimeter2 = '@@@@@@@@@@' print 'Checking %s for updates' % repo_name try: data = repo.hg_command ('incoming', '--quiet', '--template', - '{node|short} {desc}' + delimeter).split (delimeter) + delimeter.join (['{node|short}', '{desc}']) + delimeter2).split (delimeter2) except hgapi.hgapi.HgException as e: deciphered = decipher_hgapi_error (e) if deciphered[0] and len(deciphered[1]) > 0: - Irc.broadcast ("error while using hg import on %s: %s" % (repo_name, deciphered[1])) + Irc.broadcast ("error while using hg incoming on %s: %s" % (repo_name, deciphered[1])) return except Exception as e: @@ -330,20 +342,8 @@ print ('No updates to %s' % repo_name) for line in data: - if line == '': - continue - - rex = re.compile (r'([^ ]+) (.+)') - match = rex.match (line) - failed = False - - if not match: - Irc.broadcast ('malformed hg data: %s' % line) - continue - - commit_node = match.group (1) - commit_message = match.group (2) - commit_data.append ([commit_node, commit_message]) + if line: + commit_data.append (line.split (delimeter)) process_new_commits (repo_name, commit_data) @@ -357,17 +357,7 @@ num_commits = 0 zanrepo = hgapi.Repo (repo_name) print '%d new commits on %s' % (len (commit_data), repo_name) - - # Drop commits that we already have - i = 0 - while i < len (commit_data): - try: - zanrepo.hg_command ('log', '-r', commit_data[i][0]) - del commit_data[i] - except: - i += 1 - - pull_args = []; + pull_args = [] for commit in commit_data: pull_args.append ('-r'); @@ -386,12 +376,12 @@ print 'Processing new commit %s...' % commit_node try: - alreadyAdded = len (g_CommitsDb.get_commit_repos (commit_node)) > 0 + existingrepos = g_CommitsDb.get_commit_repos (commit_node) + alreadyAdded = len (existingrepos) > 0 delim = '@@@@@@@@@@' data = get_commit_data (zanrepo, commit_node, delim.join (['{author}', '{bookmarks}', \ '{date|hgdate}'])).split (delim) - print 'data is: %s' % data commit_author = data[0] commit_bookmarks = prettify_bookmarks (data[1]) commit_time = int (data[2].split (' ')[0]) @@ -402,6 +392,8 @@ # not react to it. Still add it to the db though so that the new repo name is added. g_CommitsDb.add_commit (repo=repo_name, changeset=commit_node, timestamp=commit_time) if alreadyAdded: + print ('''I already know of %s - they're in %s - not announcing.''' % + (commit_node, existingrepos)) continue # Remove the email address from the author if possible @@ -425,8 +417,8 @@ % (color_for_repo (repo_name), repo_name, commit_node, commit_bookmarks, committer, utility.shorten_link (commit_url))) - for line in commit_message.split ('\n'): - irc_client.privmsg (channel.get_value ('name'), line) + for line in commit_message.splitlines(): + irc_client.privmsg (channel.get_value ('name'), ' ' + line) if ispublishing: rex = re.compile (r'^.*(fixes|resolves|addresses|should fix) ([0-9]+).*$') diff -r f899af683bbe -r aeb0d0788869 irc.py --- a/irc.py Sat Apr 11 21:02:54 2015 +0300 +++ b/irc.py Sun Apr 19 19:45:42 2015 +0300 @@ -122,6 +122,7 @@ if line.startswith ("PING :"): self.write ("PONG :%s" % line[6:]) + self.send_all_now() # pings must be responded to immediately! else: words = line.split(" ") if len(words) >= 2: diff -r f899af683bbe -r aeb0d0788869 mod_hg.py --- a/mod_hg.py Sat Apr 11 21:02:54 2015 +0300 +++ b/mod_hg.py Sun Apr 19 19:45:42 2015 +0300 @@ -32,10 +32,17 @@ { 'name': 'resolves', - 'description': 'Manually cause a ticket to be resolved by a changeset', + 'description': '''Manually cause a ticket to be resolved by a changeset''', 'args': ' ', 'level': 'admin', # TODO }, + + { + 'name': 'rebuildcommitsdb', + 'description': '''Rebuilds commits.db''', + 'args': None, + 'level': 'admin', + }, ] } @@ -140,7 +147,6 @@ except IOError: pass - repo.hg_command ('revert', '--all') # Remove the email address from the author if possible @@ -211,7 +217,7 @@ command_error (`e`) def cmd_resolves (bot, args, **rest): - try: - HgPoll.announce_ticket_resolved (args['ticket'], args['changeset']) - except Exception as e: - command_error (str (e)) + HgPoll.announce_ticket_resolved (args['ticket'], args['changeset']) + +def cmd_rebuildcommitsdb (bot, args, **rest): + HgPoll.g_CommitsDb.create_new() \ No newline at end of file diff -r f899af683bbe -r aeb0d0788869 mod_util.py --- a/mod_util.py Sat Apr 11 21:02:54 2015 +0300 +++ b/mod_util.py Sun Apr 19 19:45:42 2015 +0300 @@ -6,6 +6,7 @@ from modulecore import command_error import modulecore as ModuleCore import utility +import calc ModuleData = { 'commands': @@ -46,6 +47,13 @@ }, { + 'name': 'calcfunctions', + 'description': 'Lists the functions supported by .calc', + 'args': None, + 'level': 'normal', + }, + + { 'name': 'more', 'description': 'Prints subsequent command result pages', 'args': None, @@ -160,30 +168,34 @@ return expr -def cmd_calc (bot, reply, args, **rest): - expr = args['expression'] +def cmd_calcfunctions (bot, reply, **rest): + reply ('Available functions for .calc: %s' % \ + ', '.join (sorted ([name for name, data in calc.Functions.iteritems()]))) - try: +def cmd_calc (bot, reply, args, **rest): + reply (calc.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 + # 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', '') + # result = subprocess.check_output (['calc', '--', expr], stderr=subprocess.STDOUT) \ + # .replace ('\t', '') \ + # .replace ('\n', '') - errmatch = re.compile (r'^.*\bError\b.*$').match (result) + # errmatch = re.compile (r'^.*\bError\b.*$').match (result) - if errmatch: - command_error ('math error') - return + # 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) + # 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) diff -r f899af683bbe -r aeb0d0788869 rest.py --- a/rest.py Sat Apr 11 21:02:54 2015 +0300 +++ b/rest.py Sun Apr 19 19:45:42 2015 +0300 @@ -100,6 +100,7 @@ try: with open ('rejected_json.txt', 'w') as fp: + fp.write (str (e)) fp.write (jsonstring) Irc.broadcast ('bad json written to rejected_json.txt') except Exception as e: diff -r f899af683bbe -r aeb0d0788869 utility.py --- a/utility.py Sat Apr 11 21:02:54 2015 +0300 +++ b/utility.py Sun Apr 19 19:45:42 2015 +0300 @@ -12,4 +12,4 @@ except Exception as e: Irc.broadcast ('Error while shortening link "%s": %s' % (link, e)) - return link + return link \ No newline at end of file