| 1 |
import os |
|---|
| 2 |
import sys |
|---|
| 3 |
import time |
|---|
| 4 |
import types |
|---|
| 5 |
import urllib |
|---|
| 6 |
|
|---|
| 7 |
from trac.core import * |
|---|
| 8 |
|
|---|
| 9 |
from tracmarket.api import * |
|---|
| 10 |
from tracmarket.mq import * |
|---|
| 11 |
from tracmarket.response import Response |
|---|
| 12 |
|
|---|
| 13 |
SequenceType = (types.ListType, types.TupleType, types.GeneratorType) |
|---|
| 14 |
|
|---|
| 15 |
|
|---|
| 16 |
|
|---|
| 17 |
class Controller(Component): |
|---|
| 18 |
implements(IMessageBusSubscriber, IMarketController) |
|---|
| 19 |
|
|---|
| 20 |
interpreters = ExtensionPoint(ICommandInterpreter) |
|---|
| 21 |
models = ExtensionPoint(IMessageBusSubscriber) |
|---|
| 22 |
ledgers = ExtensionPoint(ILedger) |
|---|
| 23 |
|
|---|
| 24 |
|
|---|
| 25 |
|
|---|
| 26 |
def solicit(self, bus): |
|---|
| 27 |
bus.subscribe('market.cmd', reader=self._route_cmd) |
|---|
| 28 |
|
|---|
| 29 |
def _route_cmd(self, bus, group, msg): |
|---|
| 30 |
'''route all incoming commands to a per-symbol group''' |
|---|
| 31 |
cmd = msg.data |
|---|
| 32 |
tokens = cmd.strip().split() |
|---|
| 33 |
symbol = tokens.pop(0) |
|---|
| 34 |
if tokens: |
|---|
| 35 |
verb = tokens.pop(0) |
|---|
| 36 |
else: |
|---|
| 37 |
verb = 'show' |
|---|
| 38 |
args = tokens |
|---|
| 39 |
msg = Msg(cmd, verb=verb, args=args) |
|---|
| 40 |
group = 'market.cmd.%s' % (symbol) |
|---|
| 41 |
bus.send(group, msg) |
|---|
| 42 |
|
|---|
| 43 |
|
|---|
| 44 |
|
|---|
| 45 |
|
|---|
| 46 |
def open(self, req=None, bus=None, db=None): |
|---|
| 47 |
if not db: |
|---|
| 48 |
db = self.env.get_db_cnx() |
|---|
| 49 |
ledger = iter(self.ledgers).next() |
|---|
| 50 |
ctx = Context(env=self.env, req=req, db=db, ledger=ledger) |
|---|
| 51 |
if not bus: |
|---|
| 52 |
bus = MemoryBus(subscribers=self.models, ctx=ctx) |
|---|
| 53 |
|
|---|
| 54 |
ctx.mkres(bus=bus) |
|---|
| 55 |
return bus |
|---|
| 56 |
|
|---|
| 57 |
def run(bus, cmds): |
|---|
| 58 |
'''Return a generator which sends commands to bus and yields |
|---|
| 59 |
the resulting message stream. Cmds is a list of command |
|---|
| 60 |
strings.''' |
|---|
| 61 |
|
|---|
| 62 |
''' |
|---|
| 63 |
# execute cmd |
|---|
| 64 |
id = bus.subscribe(re.compile('.*')) |
|---|
| 65 |
msg = Msg(cmd) |
|---|
| 66 |
bus.send('market.cmd', msg) |
|---|
| 67 |
while True: |
|---|
| 68 |
msg = bus.recv(id) |
|---|
| 69 |
if not msg: |
|---|
| 70 |
break |
|---|
| 71 |
req.hdf[msg.group] = msg.data |
|---|
| 72 |
''' |
|---|
| 73 |
|
|---|
| 74 |
def commit(self, bus): |
|---|
| 75 |
bus.ctx.db.commit() |
|---|
| 76 |
|
|---|
| 77 |
def rollback(self, bus): |
|---|
| 78 |
bus.ctx.db.rollback() |
|---|
| 79 |
|
|---|
| 80 |
def run_cmd(self, req, cmd): |
|---|
| 81 |
bus = self.open(req) |
|---|
| 82 |
bus.subscribe('res.', reader=self._recv_res) |
|---|
| 83 |
ctx = bus.ctx |
|---|
| 84 |
''' |
|---|
| 85 |
results = self.run(bus, [cmd]) |
|---|
| 86 |
for msg in results: |
|---|
| 87 |
if not msg.group.startswith('message.'): |
|---|
| 88 |
# XXX let ctx.res accumulate for now |
|---|
| 89 |
continue |
|---|
| 90 |
XXX |
|---|
| 91 |
self.close(bus) |
|---|
| 92 |
''' |
|---|
| 93 |
db = ctx.db |
|---|
| 94 |
try: |
|---|
| 95 |
self.xeq(ctx, db, cmd) |
|---|
| 96 |
except InsufficientFunds, e: |
|---|
| 97 |
db.rollback() |
|---|
| 98 |
ctx.res.error(str(e)) |
|---|
| 99 |
except: |
|---|
| 100 |
db.rollback() |
|---|
| 101 |
raise |
|---|
| 102 |
else: |
|---|
| 103 |
db.commit() |
|---|
| 104 |
return ctx |
|---|
| 105 |
|
|---|
| 106 |
|
|---|
| 107 |
|
|---|
| 108 |
def _recv_res(self, bus, group, msg): |
|---|
| 109 |
pass |
|---|
| 110 |
|
|---|
| 111 |
def xeq(self, ctx, db, cmd): |
|---|
| 112 |
req = ctx.req |
|---|
| 113 |
db = ctx.db |
|---|
| 114 |
messages = None |
|---|
| 115 |
hit = False |
|---|
| 116 |
for interpreter in self.interpreters: |
|---|
| 117 |
cmx = interpreter.parse_cmd(ctx, cmd) |
|---|
| 118 |
if cmx: |
|---|
| 119 |
hit = True |
|---|
| 120 |
cmx() |
|---|
| 121 |
break |
|---|
| 122 |
if not hit: |
|---|
| 123 |
raise TracError, "Unrecognized command: %s" % cmd |
|---|
| 124 |
|
|---|
| 125 |
|
|---|
| 126 |
class Context(object): |
|---|
| 127 |
'''A place to hold per-query context, since we shouldn't store |
|---|
| 128 |
transients in the specialist Component instance.''' |
|---|
| 129 |
|
|---|
| 130 |
def __init__(self, env, req, ledger, res=None, db=None): |
|---|
| 131 |
self.env = env |
|---|
| 132 |
self.req = req |
|---|
| 133 |
self.ledger = ledger |
|---|
| 134 |
self.res = res |
|---|
| 135 |
self.db = db |
|---|
| 136 |
|
|---|
| 137 |
def mkres(self, bus): |
|---|
| 138 |
self.res = Response(bus=bus, path='') |
|---|
| 139 |
|
|---|
| 140 |
def Mock(cls, **kwargs): |
|---|
| 141 |
d = dict( |
|---|
| 142 |
env = None, |
|---|
| 143 |
req = None, |
|---|
| 144 |
ledger = None, |
|---|
| 145 |
) |
|---|
| 146 |
d.update(kwargs) |
|---|
| 147 |
return cls(**d) |
|---|
| 148 |
Mock = classmethod(Mock) |
|---|
| 149 |
|
|---|