class MlanSearch(object): def __init__(self): self.tasklet = None def search(self, line): if self.tasklet is None: self.reqchannel = Channel() self.queued = {} self.tasklet = Tasklet(self._checker) self.tasklet() ch = Channel() if line in self.queued: self.queued[line].append(ch) else: self.queued[line] = [ch] self.reqchannel.send(line) return ch.recv() def _checker(self): while True: line = self.reqchannel.recv() waiters = self.reqchannel[line] del self.reqchannel[line] for waiter in waiters: if waiter.has_receiver(): waiter.send(["abc", "def", "ghi"])
class EmulatedHost(object): def __init__(self, dispatcher): self.dispatcher = dispatcher self._ch = Channel() def loop(self): while True: pkt = self._ch.receive() Tasklet.yield_() if pkt == [1, 2, 3]: # Test request self.dispatcher.receive([4, 5, 6]) elif pkt == ['R', 5, 1, 2, 'V']: # Version request self.dispatcher.receive([ord('R'), 2, 1, ord('V'), 88]) elif pkt == [1, 2, 5]: # Device unreachable (timeout) self.dispatcher.receive([ord('N'), 5, 1]) elif pkt == [1, 2, 6]: # Device unreachable (incorrect response) self.dispatcher.receive([ord('N'), 5, 2]) elif pkt == [1, 2, 7]: # Valid radio response self.dispatcher.receive([ord('R'), 3, 1, 4, 5, 6]) elif pkt == ['R', 5, 1, 2, 'D']: # ADC data request self.dispatcher.receive([ord('R'), 2, 1, ord('D'), 0, 0, 0, 1, 1, 0, 4, 0, 255, 255, 0, 184]) def send(self, pkt): self._ch.send(pkt) def next_pkt_id(self): return 5
class RequestQueue(object): """ This class represents abstract request queue. It has two basic operations - adding request to the queue and retrieving request to be sent just now. Different implementation may perform different algorithms of determining a the to be sent first. """ def __init__(self): self._wait = Channel() def add(self, request): "Add request to the queue" if self._wait.has_receiver(): self._wait.send(request) else: self.queue_add(request) def retrieve(self): "Retrieve request to be sent. If no requests in the queue sleep until request arrives" request = self.queue_retrieve() if request is None: request = self._wait.receive() return request def find(self, primary_key): "Find request in the internal queue by its primary key" def queue_add(self, request): "Add request to the internal queue" def queue_retrieve(self, request): "Retrieve request from the internal queue. If no request in the internal queue return None"
class WSGIInputStream(object): def __init__(self, request, reader): transfer_encoding = request.get_request_header('Transfer-Encoding') if transfer_encoding is not None and transfer_encoding == 'chunked': assert False, 'chunked post not supported yet' content_length = request.get_request_header('Content-length') if content_length is None: self._channel = None self._n = None self._file = None else: self._n = int(content_length) self._file = reader.file() self._channel = Channel() def _read_request_data(self): if self._n is not None: self._channel.receive() #wait till handler has read all input data def read(self, n = -1): if self._n > 0: data = None if n == -1: data = self._file.read(self._n) else: data = self._file.read(min(self._n, n)) self._n -= len(data) if self._n == 0: self._n = None self._file = None self._channel.send(True) #unblock reader self._channel = None return data else: return '' #EOF def readline(self): line = '' while True: data = self._file.read(1) line += data if data == '\n': break self._n -= len(line) return line def readlines(self): assert False, 'TODO' def __iter__(self): assert False, 'TODO'
class WSGIInputStream(object): def __init__(self, request, reader): transfer_encoding = request.get_request_header('Transfer-Encoding') if transfer_encoding is not None and transfer_encoding == 'chunked': assert False, 'chunked post not supported yet' content_length = request.get_request_header('Content-length') if content_length is None: self._channel = None self._n = None self._file = None else: self._n = int(content_length) self._file = reader.file() self._channel = Channel() def _read_request_data(self): if self._n is not None: self._channel.receive() #wait till handler has read all input data def read(self, n=-1): if self._n > 0: data = None if n == -1: data = self._file.read(self._n) else: data = self._file.read(min(self._n, n)) self._n -= len(data) if self._n == 0: self._n = None self._file = None self._channel.send(True) #unblock reader self._channel = None return data else: return '' #EOF def readline(self): line = '' while True: data = self._file.read(1) line += data if data == '\n': break self._n -= len(line) return line def readlines(self): assert False, 'TODO' def __iter__(self): assert False, 'TODO'
def testSendTimeout(self): #send within timeout test_channel = Channel() tl = Tasklet.later(1.0, test_channel.receive)() try: test_channel.send(10, 2.0) except TimeoutError: self.fail('did not expect timeout') finally: tl.kill() #send with timeout test_channel = Channel() tl = Tasklet.later(2.0, test_channel.receive)() try: test_channel.send(10, 1.0) self.fail('expected timeout') except TimeoutError: pass #expected finally: tl.kill()
def testLoop(self): recvd = [] def looper(channel): res = channel.receive() if res == None: raise Exception( "this is an expected exception!! (not a failed test...)") else: recvd.append(res) looper_channel = Channel() looper_task = Tasklet.loop(looper)(looper_channel) for i in range(10): looper_channel.send(i) self.assertEqual(range(10), recvd) self.assertEqual(-1, looper_channel.balance) self.assertTrue(looper_task.alive) looper_channel.send(None) #will trigger exception loop #must still be working recvd = [] for i in range(10): looper_channel.send(i) self.assertEqual(range(10), recvd) self.assertEqual(-1, looper_channel.balance) looper_task.kill() self.assertEqual(0, looper_channel.balance) #assert that looper exitted, because it is not receiving anymore self.assertFalse(looper_channel.has_receiver()) self.assertFalse(looper_task.alive)
def testLoop(self): recvd = [] def looper(channel): res = channel.receive() if res == None: raise Exception("some exception") else: recvd.append(res) looper_channel = Channel() looper_task = Tasklet.loop(looper)(looper_channel) for i in range(10): looper_channel.send(i) self.assertEqual(range(10), recvd) self.assertEqual(-1, looper_channel.balance) self.assertTrue(looper_task.alive) looper_channel.send(None) #will trigger exception loop #must still be working recvd = [] for i in range(10): looper_channel.send(i) self.assertEqual(range(10), recvd) self.assertEqual(-1, looper_channel.balance) looper_task.kill() self.assertEqual(0, looper_channel.balance) #assert that looper exitted, because it is not receiving anymore self.assertFalse(looper_channel.has_receiver()) self.assertFalse(looper_task.alive)
class HttpPerf(object): def __init__(self, options): self.status = {} self.request = 0 self.lastRequest = None self.lastTime = None self.options = options self.dispenser = Channel() def session_response_reader(self, cnn, pipeline_tokens): #TODO use tasklet.loop, must be extended such that you can stop the loop by returning something (or StopIteration?) while True: response = cnn.receive() #read status self.count('status', response.status) connection_header = response.get_header('Connection') if connection_header == 'close' and self.options.requests != 1: print >> sys.stderr, "WARNING: Server closed connection, no Keep Alive!, please use --requests=1" #this will read the complete response if self.options.dump: print response.status for k, v in response.headers: print "%s: %s" % (k, v) for chunk in response: sys.stdout.write(chunk) sys.stdout.flush() print else: list(response) #print 'resp' pipeline_tokens.append(True) def session(self, host, port, path): cnn = None pipeline_tokens = Deque() for _ in range(self.options.pipeline): # can append take iterator?, or list? pipeline_tokens.append(True) try: cnn = HTTPConnection() cnn.connect((host, port)) Tasklet.new(self.session_response_reader)(cnn, pipeline_tokens) requests = 0 #no requests in this session while True: if self.options.requests != -1 and requests >= self.options.requests: break #we are done with this session if self.dispenser.receive() is None: return False #we are done globally pipeline_tokens.popleft(True) #do the request cnn.send(cnn.get(path)) #print response requests += 1 self.count('request') finally: #if response_reader_task is not None: # response_reader_task.kill() if cnn is not None: cnn.close() return True def sessions(self): u = urlparse.urlparse(self.options.url) if ':' in u.netloc: host, port = u.netloc.split(':') port = int(port) else: host, port = u.netloc, 80 path = urlparse.urlunsplit(['', '', u.path, u.query, u.fragment]) if path == '': path = '/' try: while True: if not self.session(host, port, path): return except TaskletExit: raise except: logging.exception("exception in http session") def count(self, attr, key = None, inc = 1): a = getattr(self, attr) if key is None: v = a + inc setattr(self, attr, v) return v else: if not key in a: a[key] = inc else: a[key] = a[key] + inc return a[key] def show(self): now = time.time() if self.lastTime is not None: reqSec = (self.request - self.lastRequest) / (now - self.lastTime) reqSec = gamma_filter(self.lastReqSec, reqSec, 0.60) else: reqSec = 0.0 print >> sys.stderr, self.status, self.request, reqSec self.lastTime = time.time() self.lastRequest = self.request self.lastReqSec = reqSec def dispense(self): if self.options.count == -1: #run forever while True: self.dispenser.send(True) if self.options.delay > 0.0: Tasklet.sleep(self.options.delay) else: #a fixed number of total requests for i in range(self.options.count): self.dispenser.send(True) if self.options.delay > 0.0: Tasklet.sleep(self.options.delay) for i in range(self.options.sessions): self.dispenser.send(None) def run(self): #show stats every second: Tasklet.interval(1.0, self.show, immediate = True)() #dispenses tokens for doing a request to sessions: Tasklet.new(self.dispense)() #start up sessions, and wait till they are finished Tasklet.join_all([Tasklet.new(self.sessions)() for _ in range(self.options.sessions)]) quit()
class WSGIInputStream(object): def __init__(self, request, reader): transfer_encoding = request.get_request_header('Transfer-Encoding') if transfer_encoding is not None and transfer_encoding == 'chunked': assert False, 'chunked post not supported yet' content_length = request.get_request_header('Content-length') if content_length is None: self._channel = None self._n = None self._file = None else: self._n = int(content_length) self._file = reader.file() self._channel = Channel() self.readline_buffer = None def _read_request_data(self): if self._n is not None: self._channel.receive() #wait till handler has read all input data def read(self, n): if self._n > 0: data = self._file.read(min(self._n, n)) self._n -= len(data) if self._n == 0: self._n = None self._file = None self._channel.send(True) #unblock reader self._channel = None return data else: return '' #EOF def readline(self, maxlen=-1): if self.readline_buffer is None: self.readline_buffer = '' self.re_line = re.compile(r'^(.*?(?:\r\n|\n))(.*)', re.DOTALL) while True: m = self.re_line.match(self.readline_buffer) if m: line, self.readline_buffer = m.group(1, 2) return line elif self._n <= 0: if len(self.readline_buffer): line = self.readline_buffer self.readline_buffer = '' return line return None data = self._file.read(min(self._n, 16384)) if len(data) == 0: self._n = 0 else: self._n -= len(data) self.readline_buffer = self.readline_buffer + data def readlines(self): assert False, 'TODO' def __iter__(self): assert False, 'TODO'
class Combat(mg.constructor.ConstructorModule, CombatParamsContainer): "Combat is the combat itself. It is created in the combat daemon process." system_params = set(["stage", "title", "time", "timetext"]) def __init__(self, app, uuid, rules, fqn="mg.mmorpg.combats.core.Combat"): mg.constructor.ConstructorModule.__init__(self, app, fqn) CombatParamsContainer.__init__(self) self.members = [] self.log = None self.member_id = 0 self.rules = rules self.uuid = uuid self.controllers = [] self.rulesinfo = self.conf("combats-%s.rules" % rules, {}) self.paramsinfo = self.conf("combats-%s.params" % rules, {}) self.actionsinfo = self.conf("combats-%s.actions" % rules, []) self.commands = [] self.wakeup_channel = Channel() self.running_actions = [] self.ready_actions = [] self._turn_order_check = False self._check_end_condition = True self.not_delivered_log = [] self.start_time = time.time() self._flags = set() self._textlog_ring = [] def script_code(self, tag): "Get combat script code (syntax tree)" return self.conf("combats-%s.script-%s" % (self.rules, tag), []) def join(self, member): "Join member to the combat" self.member_id += 1 member.id = self.member_id self.members.append(member) # script event globs = self.globs() globs["member"] = member self.execute_script("joined", globs, lambda: self._("Member joined script")) # if combat is started already, notify all other members if self.running: for controller in self.controllers: if controller.connected: controller.deliver_member_joined(member) # register member's controllers for controller in member.controllers: self.add_controller(controller) # log join self.syslog({ "type": "join", "member": member.id, "text": self._("<b>[{time}]</b> Member {id} ({name}) has joined team {team}").format( time=self.now(), id=member.id, name=member.name, team=member.team, ), "cls": "combat-syslog-joined", }) def member(self, memberId): for m in self.members: if m.id == memberId: return m return None def close(self): "Notify combat about it's terminated and about to be destroyed" if self.log: self.log.close() self.flush() @property def actions(self): "Dictionary of available combat actions" try: return self._actions except AttributeError: pass self._actions = {} for act in self.conf("combats-%s.actions" % self.rules, []): self._actions[act["code"]] = act return self._actions @property def running(self): "True when combat is running" return self.stage != "init" def run(self, turn_order): """ Run combat (switch to 'combat' stage). turn_order - CombatTurnOrder object """ if self.running: raise CombatAlreadyRunning(self._("Combat was started twice")) self.turn_order = turn_order self.set_stage("combat") self.log_combat_time() # execute start script globs = self.globs() self.execute_script("start", globs, lambda: self._("Combat start script")) # notify all members for member in self.members: member.started() # notify turn order manager if self.stage_flag("actions"): self.turn_order.start() @property def stage(self): return self._params.get("stage", "init") def set_stage(self, stage): "Switch combat stage" if self.stages.get(stage) is None: raise CombatInvalidStage(self._("Combat stage '%s' is not defined") % stage) self.set_param("stage", stage) self.syslog({ "type": "stage", "stage": stage, "text": self._("Combat stage: %s") % stage, "cls": "combat-syslog-stage", }) self.wakeup() @property def flags(self): return self._flags def set_flags(self, flags): self._flags = set(flags) @property def title(self): return self._params.get("title", self._("Combat")) def set_title(self, title): "Set combat title" self.set_param("title", title) if self.log: self.log.set_title(title) self.wakeup() @property def timetext(self): time_format = self.rulesinfo.get("time_format", "mmss") time = self.time if time_format == "mmss": return "%d:%02d" % (time / 60, time % 60) elif time_format == "num": return self.time elif time_format == "realhhmmss": return self.now_local().split(" ")[1] @property def time_mode(self): try: return self._time_mode except AttributeError: pass self._time_mode = self.rulesinfo.get("time_mode", "begin") return self._time_mode @property def time(self): return self._params.get("time", 0) def log_combat_time(self): self.syslog({ "text": self._("Combat time: %s") % self.time, "time": self.time, "cls": "combat-syslog-time", }) if self.time_mode == "change": self.textlog({ "text": self.timetext, "cls": "combat-log-time-header", }) def add_time(self, val): val = intz(val) if val < 1: return self.set_param("time", self.time + val) self.set_param("timetext", self.timetext) self.log_combat_time() def add_controller(self, controller): "Register member controller" self.controllers.append(controller) @property def stages(self): "Dictionary of stages and their flags" try: return self._stages except AttributeError: pass val = self.conf("combats-%s.stages" % self.rules) if val is None: val = { "init": { }, "combat": { "actions": True, }, "finish": { }, "done": { "done": True } } self._stages = val return val def stage_flag(self, flag): "Returns flag value of the current stage. If no flag with such code defined return None" return self.stages[self.stage].get(flag) def stopped(self): "Return True when the combat is stopped" return self.stage_flag("done") def add_command(self, command): "Put command to the combat queue to be executed immediately" self.commands.append(command) self.wakeup() def wakeup(self): "Wake up main combat loop if it's busy with processing now" if self.wakeup_channel.has_receiver(): self.wakeup_channel.send(None) def process(self, timeout=1): "Process combat logic" if self._turn_order_check: self._turn_order_check = False self.turn_order.check() if self._check_end_condition: self._check_end_condition = False self.check_end_condition() self.process_commands() if self.stage_flag("actions"): self.process_actions() self.heartbeat() self.flush() try: self.wakeup_channel.receive(timeout) except TimeoutError: self.idle() def globs(self): return { "local": ScriptMemoryObject() } def execute_script(self, tag, globs, description=None): "Execute combat script with given code" self.call("combats.execute-script", self, self.script_code(tag), globs, description=description) def execute_member_script(self, member, tag, globs, description=None): "Execute combat script for given member" globs["member"] = member self.execute_script(tag, globs, description) self.enqueue_check_end_condition() def heartbeat(self): "Called on every iteration of the main loop" globs = self.globs() self.for_each_member(self.execute_member_script, "heartbeat-member", globs, lambda: self._("Member heartbeat script")) self.execute_script("heartbeat", globs, lambda: self._("Combat heartbeat script")) def process_commands(self): "Process enqueued commands" while self.commands: cmd = self.commands.pop(0) cmd.execute() def idle(self): "Do background processing" # execute scripts globs = self.globs() self.execute_script("idle", globs, lambda: self._("Combat idle script")) self.for_each_member(self.execute_member_script, "idle-member", globs, lambda: self._("Member idle script")) # call idle for all objects self.turn_order.idle() for member in self.members: member.idle() # process general timeouts elapsed = time.time() - self.start_time timeout = self.rulesinfo.get("timeout", 4 * 3600) if elapsed > timeout + 600: self.warning(self._("Combat %s terminated due to too long timeout"), self.uuid) os._exit(0) elif self.stage_flag("actions") and elapsed > timeout: self.info(self._("Combat %s timed out"), self.uuid) self.draw() def flush(self): "Flush pending messages" # deliver changed parameters params = self.changed_params() if params: for controller in self.controllers: controller.combat_params_changed(params) for member in self.members: params = member.changed_params() if params: for controller in self.controllers: controller.member_params_changed(member, params) # commit logs if self.log: self.log.flush() # deliver new log entries if self.not_delivered_log: for controller in self.controllers: controller.deliver_log(self.not_delivered_log) self.not_delivered_log = [] # flush everything to the clients for controller in self.controllers: controller.flush() self.call("stream.flush") def set_log(self, log): "Attach logging system to the combat" self.log = log def textlog(self, entry): "Add entry to combat log" if self.time_mode == "begin": entry["text"] = u'<span class="combat-log-time">%s</span> %s' % (self.timetext, entry.get("text", u"")) self.not_delivered_log.append(entry) if self.log: self.log.textlog(entry) self._textlog_ring.append(entry) l = len(self._textlog_ring) if l > textlog_ring_size: del self._textlog_ring[0:l - textlog_ring_size] def syslog(self, entry): "Add entry to combat debug log" if self.log: self.log.syslog(entry) def stop(self): "Terminate combat" self.set_stage("done") # Scripting def script_attr(self, attr, handle_exceptions=True): if attr == "id": return self.uuid elif attr == "stage": return self.stage elif attr == "stage_flags": return CombatStageFlags(self) elif attr == "time": return self.time elif attr == "timetext": return self.timetext elif attr == "now": return self.now_local() # team list m = re_team_list.match(attr) if m: team = intz(m.group(1)) return self.call("l10n.literal_enumeration", [u'<span class="combat-log-member">%s</span>' % member.name for member in self.members if member.team == team]) # parameters m = re_param_attr.match(attr) if m: return self.param(attr, handle_exceptions) if handle_exceptions: return None else: raise AttributeError(attr) def script_set_attr(self, attr, val, env): if attr == "stage": return self.set_stage(val) # parameters m = re_param_attr.match(attr) if m: return self.set_param(attr, val) raise ScriptRuntimeError(self._("Invalid attribute '%s'") % attr, env) def store(self): pass # Actions def execute_action(self, action): "Start executing action" self.ready_actions.append(action) def process_actions(self): "Process actions logic" self.process_ready_actions() self.process_stopped_actions() def process_ready_actions(self): """ For every ready action call begin() method and move the action to the list of running actions """ if self.ready_actions: actions = self.ready_actions self.ready_actions = [] for act in actions: if act.source.active: act.begin() self.running_actions.append(act) self.enqueue_turn_order_check() self.actions_started() self.enqueue_check_end_condition() def process_stopped_actions(self): "For every stopped action call end() method and remove the action from the list" if self.running_actions: i = 0 while i < len(self.running_actions): act = self.running_actions[i] if act.stopped(): act.end() del self.running_actions[i] else: i += 1 self.enqueue_turn_order_check() self.actions_stopped() self.enqueue_check_end_condition() def enqueue_check_end_condition(self): "Enqueue check_end_condition() to be called on the next iteration of the main loop" self._check_end_condition = True self.wakeup() def check_end_condition(self): "Check combat end condition (0 or 1 teams active)" if self.stage_flag("actions"): teams = set() for member in self.members: if member.active: teams.add(member.team) teams = list(teams) if len(teams) == 0: self.draw() elif len(teams) == 1: self.victory(teams[0]) def draw(self): "Combat finished with draw" self.syslog({ "type": "draw", "text": self._("Combat was a draw"), "cls": "combat-syslog-end", }) for member in self.members: member.draw() globs = self.globs() self.for_each_member(self.execute_member_script, "draw-member", globs, lambda: self._("Combat draw script for a member")) self.execute_script("draw", globs, lambda: self._("Combat draw script")) def victory(self, team): "Combat finished with victory of specified team" self.syslog({ "type": "victory", "team": team, "text": self._("Victory of team {team}").format( team=team, ), "cls": "combat-syslog-end", }) winners_list = [] loosers_list = [] first_winner = None first_looser = None for member in self.members: if member.team != team: member.defeat() loosers_list.append(member) if first_looser is None: first_looser = member for member in self.members: if member.team == team: member.victory() winners_list.append(member) if first_winner is None: first_winner = member globs = self.globs() globs["winner_team"] = team globs["winners_list"] = self.call("l10n.literal_enumeration", [u'<span class="combat-log-member">%s</span>' % member.name for member in winners_list]) globs["loosers_list"] = self.call("l10n.literal_enumeration", [u'<span class="combat-log-member">%s</span>' % member.name for member in loosers_list]) globs["first_winner"] = first_winner globs["first_looser"] = first_looser globs["winners_count"] = len(winners_list) globs["loosers_count"] = len(loosers_list) for member in self.members: if member.team != team: self.execute_member_script(member, "defeat-member", globs, lambda: self._("Combat defeat script for a member")) for member in self.members: if member.team == team: self.execute_member_script(member, "victory-member", globs, lambda: self._("Combat victory script for a member")) self.execute_script("victory", globs, lambda: self._("Combat victory script")) def notify_stopped(self): "Call this method to signal combat that it's finally stopped" for member in self.members: if member.may_turn: member.turn_take() for member in self.members: member.stopped() def actions_started(self): "Called after ready actions started" globs = self.globs() self.for_each_member(self.execute_member_script, "actions-started-member", globs, lambda: self._("Combat actions started script for a member")) self.execute_script("actions-started", globs, lambda: self._("Combat actions started script")) def actions_stopped(self): "Called after ready actions stopped" globs = self.globs() self.for_each_member(self.execute_member_script, "actions-stopped-member", globs, lambda: self._("Combat actions stopped script for a member")) self.execute_script("actions-stopped", globs, lambda: self._("Combat actions stopped script")) def enqueue_turn_order_check(self): "Ask combat server to call turn_order check() on the next iteration" self._turn_order_check = True self.wakeup() def for_each_member(self, callback, *args, **kwargs): "Call callback for every combat member. Member is passed as a first argument" for member in self.members: callback(member, *args, **kwargs) def __unicode__(self): return self._("[Combat %s]") % self.uuid def __str__(self): return utf2str(unicode(self))