def _addsvc(cls, svc): if svc.name in cls._svcs_dict: raise llbc.error('service name repeat, name: {}'.format(svc.name)) elif hasattr(cls, svc.name): raise llbc.error('service name is used to Service class attribute, name: {}'.format(svc.name)) cls._svcs_list.append(svc) cls._svcs_dict[svc.name] = svc
def __init__(self, ty, svc, handler): if not self.is_validate_handler_type(ty): raise llbc.error('invalidate handler type: {}'.format(ty)) elif svc is None or (handler is None or not callable(handler)): raise llbc.error('service[{}] invalidate or handler[{}] invalidate'.format(svc, handler)) self._ty = ty self._svc = _ref(svc) self._handler = handler
def registerencoder(self, opcode, encoder): """ Register specific opcode's encoder(only available in CODEC_BINARY codec mode). """ if not hasattr(encoder, 'encode') or not callable(encoder.encode): raise llbc.error('invalid encoder, opcode: {}, encoder: {}'.format(opcode, encoder)) elif encoder in self._encoders: raise llbc.error('encoder duplicate registered, opcode: {}, encoder: {}'.format(opcode, encoder)) self._encoders.update({encoder:opcode})
def _addsvc(cls, svc): if svc.name in cls._svcs_dict: raise llbc.error('service name repeat, name: {}'.format(svc.name)) elif hasattr(cls, svc.name): raise llbc.error( 'service name is used to Service class attribute, name: {}'. format(svc.name)) cls._svcs_list.append(svc) cls._svcs_dict[svc.name] = svc
def __init__(self, ty, svc, handler): if not self.is_validate_handler_type(ty): raise llbc.error('invalidate handler type: {}'.format(ty)) elif svc is None or (handler is None or not callable(handler)): raise llbc.error( 'service[{}] invalidate or handler[{}] invalidate'.format( svc, handler)) self._ty = ty self._svc = _ref(svc) self._handler = handler
def registerfacade(self, facade): """ Register facade. facade methods(all methods are optional): oninitialize(self, ev): service initialize handler. ev.svc: service object. ondestroy(self, ev): service destroy handler. ev.svc: service object. onupdate(self, ev): service per-frame update handler. ev.svc: service object. onidle(self, ev): service per-frame idle handler. ev.svc: service object. ev.idletime: idle time, float type, in seconds. onsessioncreate(self, ev): session create handler. ev.svc: service object. ev.islisten: is listen session or not. ev.session_id: session Id. ev.local_ip: local ip address. ev.local_port: local port number. ev.peer_ip: peer ip address. ev.peer_port: peer port number. onsessiondestroy(self, ev): session destroy handler. ev.svc: service object. ev.session_id: session Id. onasyncconnresult(self, ev): async-connect result handler. ev.svc: service object. ev.peer_ip: peer ip address. ev.peer_port: peer port number. ev.connected: connected flag. ev.reason: reason describe. onprotoreport(self, ev): protocol report. ev.svc: service object. ev.report_layer: which layer protocol reported. ev.report_level: report event level(DEBUG, INFO, WARN, ERROR). ev.report_msg: report message. ev.session_id: report session_id(optional, maybe is 0). onunhandledpacket(self, ev): unhandled packet. ev.svc: service object. ev.opcode: packet opcode. """ if hasattr(facade, '__bases__') and llbc.ischild(facade, type): raise llbc.error('facade could not be type(or derived from type) instance, facade:{}'.format(facade)) elif isinstance(facade, type): raise llbc.error('facade could not be class type object, facade: {}'.format(facade)) else: if isinstance(facade, _types.FunctionType): raise llbc.error('facade could not be a function, facade: {}'.format(facade)) elif isinstance(facade, _types.MethodType): raise llbc.error('facade could not be a method(included bound and unbound), facade: {}'.format(facade)) llbc.inl.RegisterFacade(self._c_obj, facade) self._facades.update({facade.__class__: facade})
def registerencoder(self, opcode, encoder): """ Register specific opcode's encoder(only available in CODEC_BINARY codec mode). """ if not hasattr(encoder, 'encode') or not callable(encoder.encode): raise llbc.error('invalid encoder, opcode: {}, encoder: {}'.format( opcode, encoder)) elif encoder in self._encoders: raise llbc.error( 'encoder duplicate registered, opcode: {}, encoder: {}'.format( opcode, encoder)) self._encoders.update({encoder: opcode})
def __init__(self, ob): ob_type = pyllbcObservable.ObserverType if callable(ob): if type(ob) == MethodType: if not ob.__self__: raise llbc.error('observer is MethodType, but method unbound') self.type = ob_type.Method self.ref = ref(ob.__self__) self.func = ob.im_func else: self.type = ob_type.Callable self.ref = ref(ob) else: raise llbc.error('observer not callable, observer: {}'.format(ob))
def __setattr__(cls, key, value): if key in cls._svcs_dict: raise llbc.error( 'attribute [{}] already used by service name, could not override' .format(key)) else: super(pyllbcServiceMetaCls, cls).__setattr__(key, value)
def send(self, session_id, data, opcode=None, status=0, extData1=0, extData2=0, extData3=0): """ Send data to specific session """ cls = self.__class__ svc_type = self._svctype if svc_type == cls.NORMAL: if opcode is None: opcode = self._encoders.get(data.__class__) if opcode is None: raise llbc.error( 'will send data not specific opcode(forgot use "@llbc.forsend" decorator?), ' 'data: {}, type: {}'.format(data, data.__class__)) else: opcode = 0 # Support using packet as session_id to send packet. if isinstance(session_id, Packet): session_id = session_id.session_id llbc.inl.SendData(self._c_obj, session_id, opcode, data, status, extData1, extData2, extData3)
def schedule2(self, duetime, period, fmtstr='%Y-%m-%d %H:%M:%S'): """ Schedule timer, arguments is datetime type object, str type object, or numeric type object, if duetime type is datetime type object, will use it as expire time. if duetime type is str type, will convert to datetime type to use. if duetime type is numeric type, will as timestamp to use, as seconds. If not specified the tzinfo, llbc will automatic use local tzinfo to fill. """ if isinstance(duetime, unicode): duetime = duetime.decode('utf-8') if isinstance(duetime, str): duetime = _dt.strptime(duetime, fmtstr) if isinstance(duetime, _dt): ts = _time.mktime( duetime.timetuple()) + duetime.microsecond / 1000000.0 else: ts = duetime now = _time.time() if ts < now: raise llbc.error( 'duetime[{}] < nowtime[{}], schedule timer failed'.format( duetime, _dt.fromtimestamp(now))) self.schedule(int((ts - now) * 1000), int(period * 1000))
def __init__(self, ob): ob_type = pyllbcObservable.ObserverType if callable(ob): if type(ob) == MethodType: if not ob.__self__: raise llbc.error( 'observer is MethodType, but method unbound') self.type = ob_type.Method self.ref = ref(ob.__self__) self.func = ob.im_func else: self.type = ob_type.Callable self.ref = ref(ob) else: raise llbc.error( 'observer not callable, observer: {}'.format(ob))
def set_header_desc(header_desc): """ Set packet header describe. """ if not isinstance(header_desc, llbc.PacketHeaderDesc): raise llbc.error('set header desc failed, type error, given type: {}'.format(type(header_desc))) llbc.inl.SetPacketHeaderDesc(header_desc._cobj)
def add_part(self, part_desc): if not isinstance(part_desc, _llbc.PacketHeaderPartDesc): raise _llbc.error('part desc type error, type: {}'.format(type(part_desc))) _inl.PacketHeaderDesc_AddPart(self._cobj, part_desc._cobj) self._parts[part_desc.serial_no] = part_desc self._update_bookkeeping()
def __init__(self, svcname, svctype=llbc.inl.SVC_TYPE_NORMAL): """ Create new service :param svctype: the service type, see service type enumeration. :param svcname: the service name, must be not empty. """ if isinstance(svcname, unicode): svcname = svcname.encode('utf-8') if not isinstance(svcname, str): raise llbc.error('service name must str type') elif not svcname: raise llbc.error('service name must be not empty') if self.isgonetolimit(): raise llbc.error('service count is gone to limit') self._svcname = svcname self._svctype = svctype self._addsvc(self) self._c_obj = llbc.inl.NewService(self, svctype, svcname) self._svcid = llbc.inl.GetServiceId(self._c_obj) self._encoders = {} self._facades = {} cobj = self._c_obj self._fps = llbc.inl.GetServiceFPS(cobj) self._frameinterval = llbc.inl.GetServiceFrameInterval(cobj) self._last_schedule_time = 0 self._started = False self._terminating = False self._terminated = False self._subscribe_exc_handlers = dict( ) # key: opcode, value: exception handler self._presubscribe_exc_handlers = dict( ) # key: opcode, value: exception handler self._dft_subscribe_exc_handler = None self._dft_presubscribe_exc_handler = None
def schedule(cls, schedule_time=0, idle_sleep=True): """ Service mainloop :param schedule_time: specific schedule time, in seconds, if less than or equal to 0, schedule or return immediately. :param idle_sleep: at one schedule operation, if service check has idle time, will sleep if idle_sleep set to True, otherwise not sleep. """ if cls._scheduling: raise llbc.error('Service in scheduling, not allow to reschedule') cls._scheduling = True svcs = cls._svcs_list schedule_interval = 1.0 / cls.MAX_FPS svc_mainloop = llbc.inl.ServiceMainLoop update_timers = llbc.inl.PyTimerUpdateAllTimers inst_errhooker = llbc.inl.InstallErrHooker uninst_errhooker = llbc.inl.UninstallErrHooker clear_hookederrors = llbc.inl.ClearHookedErrors try: inst_errhooker() cls._procwilldelsvcs() if schedule_time > 0: schedule_beg = _pyllbc_time() while True: if idle_sleep: scheduleonce_beg = _pyllbc_time() clear_hookederrors() try: for svc in svcs: if not svc._started: continue svc_beg = _pyllbc_time() if svc_beg - svc._last_schedule_time >= svc._frameinterval: cobj = svc._c_obj try: svc_mainloop(cobj) except Exception, e: frame_exc_handler = cls._frame_exc_handler if frame_exc_handler is not None: tb = _sys.exc_info()[2] frame_exc_handler(svc, tb, e) else: raise finally: svc._last_schedule_time = svc_beg if cls._procpendingdeschedule(): return update_timers() if cls._procpendingdeschedule(): return
def set_header_desc(header_desc): """ Set packet header describe. """ if not isinstance(header_desc, llbc.PacketHeaderDesc): raise llbc.error( 'set header desc failed, type error, given type: {}'.format( type(header_desc))) llbc.inl.SetPacketHeaderDesc(header_desc._cobj)
def pyllbc_extractreg(wrapped, ty): """ Extract wrapped class/function library register info data, if not exist, will create it. """ if not isinstance(wrapped, (TypeType, ClassType)): raise llbc.error('@forsend/@forrecv/@handler/@prehandler/@unify_prehandler/@exc_handler/@exc_prehandler/@bindto decorator ' \ 'must decorate class type object, could not decorate {}'.format(type(wrapped))) RegCls = llbc.inl.SvcRegInfo libkey = '__pyllbcreg__' if not hasattr(wrapped, libkey): setattr(wrapped, libkey, RegCls(wrapped)) reg = getattr(wrapped, libkey) if reg.regtype == RegCls.UnSpecific: reg.regtype = ty elif ty != RegCls.UnSpecific and ty != reg.regtype: raise llbc.error('conflict to use @handler/@prehandler/@unify_prehandler/@exc_handler/@exc_prehandler \ and @forsend/@forrecv decorator in the same class: {}'.format(wrapped)) return reg
def __init__(self, svcname, svctype=llbc.inl.SVC_TYPE_NORMAL): """ Create new service :param svctype: the service type, see service type enumeration. :param svcname: the service name, must be not empty. """ if not isinstance(svcname, str): raise llbc.error('service name must str type') elif not svcname: raise llbc.error('service name must be not empty') if self.isgonetolimit(): raise llbc.error('service count is gone to limit') self._svcname = svcname self._svctype = svctype self._addsvc(self) self._c_obj = llbc.inl.NewService(self, svctype) self._encoders = {} self._facades = {} cobj = self._c_obj self._fps = llbc.inl.GetServiceFPS(cobj) self._frameinterval = llbc.inl.GetServiceFrameInterval(cobj) self._last_schedule_time = 0 self._started = False self._terminating = False self._terminated = False self._subscribe_exc_handlers = dict() # key: opcode, value: exception handler self._presubscribe_exc_handlers = dict() # key: opcode, value: exception handler self._dft_subscribe_exc_handler = None self._dft_presubscribe_exc_handler = None
def set_default_presubscribe_exc_handler(self, dft_exc_handler): """ Set the packet pre-handler's default exception handler. handler proto-type: the_packet_exception_handler(packet, traceback_obj, exception_value) packet: the packet object. traceback_obj: the traceback type instance. error_value: the exception value. """ if dft_exc_handler is None: self._dft_presubscribe_exc_handler = None elif not callable(dft_exc_handler): raise llbc.error('{} not callable'.format(dft_exc_handler)) else: self._dft_presubscribe_exc_handler = dft_exc_handler
def pyllbc_extractreg(wrapped, ty): """ Extract wrapped class/function library register info data, if not exist, will create it. """ if not isinstance(wrapped, (TypeType, ClassType, FunctionType)): raise llbc.error('@forsend/@forrecv/@handler/@prehandler/@unify_prehandler/@exc_handler/@exc_prehandler/@bindto decorator ' \ 'must decorate class type object or function type object, could not decorate {}'.format(type(wrapped))) RegCls = llbc.inl.SvcRegInfo libkey = '__pyllbcreg__' if not hasattr(wrapped, libkey): setattr(wrapped, libkey, RegCls(wrapped)) reg = getattr(wrapped, libkey) if reg.regtype == RegCls.UnSpecific: reg.regtype = ty elif ty != RegCls.UnSpecific and ty != reg.regtype: raise llbc.error( 'conflict to use @handler/@prehandler/@unify_prehandler/@exc_handler/@exc_prehandler \ and @forsend/@forrecv decorator in the same class: {}'.format( wrapped)) return reg
def schedule(cls): """ Service mainloop """ if cls._scheduling: raise llbc.error('Service in scheduling, not allow to reschedule') cls._scheduling = True svcs = cls._svcs_list schedule_interval = 1.0 / cls.MAX_FPS svc_mainloop = llbc.inl.ServiceMainLoop update_timers = llbc.inl.PyTimerUpdateAllTimers inst_errhooker = llbc.inl.InstallErrHooker uninst_errhooker = llbc.inl.UninstallErrHooker clear_hookederrors = llbc.inl.ClearHookedErrors try: inst_errhooker() cls._procwilldelsvcs() while True: clear_hookederrors() schedule_beg = _pyllbc_time() try: for svc in svcs: if not svc._started: continue svc_beg = _pyllbc_time() if svc_beg - svc._last_schedule_time >= svc._frameinterval: cobj = svc._c_obj try: svc_mainloop(cobj) except Exception, e: frame_exc_handler = cls._frame_exc_handler if frame_exc_handler is not None: tb = _sys.exc_info()[2] frame_exc_handler(svc, tb, e) else: raise finally: svc._last_schedule_time = svc_beg if cls._procpendingdeschedule(): return update_timers() if cls._procpendingdeschedule(): return
def set_frame_exc_handler(cls, exc_handler): """ Set service per-frame exception handler(class method). handler can be function or method or callable object. handler method proto-type: the_frame_exception_handler(service_obj, traceback_obj, exception_value) service_obj: the service instance, this params maybe None if raised in timer. traceback_obj: the traceback type instance. error_value: the exception value. """ if exc_handler is None: cls._frame_exc_handler = None elif not callable(exc_handler): raise llbc.error('{} not callable'.format(exc_handler)) else: cls._frame_exc_handler = exc_handler
def set_presubscribe_exc_handler(self, opcode, exc_handler): """ Set the packet pre-handler's exception handler. handler proto-type: the_packet_exception_handler(packet, traceback_obj, exception_value) packet: the packet object. traceback_obj: the traceback type instance. error_value: the exception value. """ if exc_handler is None: if opcode in self._presubscribe_exc_handlers: del self._presubscribe_exc_handlers[opcode] elif not callable(exc_handler): raise llbc.error('{} not callable'.format(exc_handler)) else: self._presubscribe_exc_handlers[opcode] = exc_handler
def wrapper(*call_varargs, **call_kwargs): call_args = _get_call_args(arg_names, vararg_name, kw_name, call_varargs, call_kwargs) k, item = None, None try: for k in validator: if k == vararg_name: for item in call_args[k]: assert validator[k](item) elif k == kw_name: for item in call_args[k].itervalues(): assert validator[k](item) else: item = call_args[k] assert validator[k](item) except: raise error('{}() parameter validation failed, param: {}, value: {}({})' .format(func.func_name, k, item, item.__class__.__name__)) return func(*call_varargs, **call_kwargs)
def wrapper(*call_varargs, **call_kwargs): call_args = _get_call_args(arg_names, vararg_name, kw_name, call_varargs, call_kwargs) k, item = None, None try: for k in validator: if k == vararg_name: for item in call_args[k]: assert validator[k](item) elif k == kw_name: for item in call_args[k].itervalues(): assert validator[k](item) else: item = call_args[k] assert validator[k](item) except: raise error( '{}() parameter validation failed, param: {}, value: {}({})' .format(func.func_name, k, item, item.__class__.__name__)) return func(*call_varargs, **call_kwargs)
def send(self, session_id, data, opcode=None, status=0, parts=None): """ Send data to specific session """ cls = self.__class__ svc_type = self._svctype if svc_type == cls.NORMAL: if opcode is None: opcode = self._encoders.get(data.__class__) if opcode is None: raise llbc.error('will send data not specific opcode(forgot use "@llbc.forsend" decorator?), ' 'data: {}, type: {}'.format(data, data.__class__)) else: opcode = 0 # Support using packet as session_id to send packet. if isinstance(session_id, Packet): session_id = session_id.session_id if parts is None: llbc.inl.SendData(self._c_obj, session_id, opcode, data, status) else: llbc.inl.SendData(self._c_obj, session_id, opcode, data, status, parts)
def schedule2(self, duetime, period, fmtstr='%Y-%m-%d %H:%M:%S'): """ Schedule timer, arguments is datetime type object, str type object, or numeric type object, if duetime type is datetime type object, will use it as expire time. if duetime type is str type, will convert to datetime type to use. if duetime type is numeric type, will as timestamp to use, as seconds. If not specified the tzinfo, llbc will automatic use local tzinfo to fill. """ if isinstance(duetime, unicode): duetime = duetime.decode('utf-8') if isinstance(duetime, str): duetime = _dt.strptime(duetime, fmtstr) if isinstance(duetime, _dt): ts = _time.mktime(duetime.timetuple()) + duetime.microsecond / 1000000.0 else: ts = duetime now = _time.time() if ts < now: raise llbc.error('duetime[{}] < nowtime[{}], schedule timer failed'.format(duetime, _dt.fromtimestamp(now))) self.schedule(int((ts - now) * 1000), int(period * 1000))
def _startcheck(self): if self._started: raise llbc.error('service object already started') elif self._terminating: raise llbc.error('service object terminating')
def __setattr__(cls, key, value): if key in cls._svcs_dict: raise llbc.error('attribute [{}] already used by service name, could not override'.format(key)) else: super(pyllbcServiceMetaCls, cls).__setattr__(key, value)
def registerfacade(self, facade): """ Register facade. facade methods(all methods are optional): oninitialize(self, ev): service initialize handler. ev.svc: service object. ondestroy(self, ev): service destroy handler. ev.svc: service object. onstart(self, ev): service start handler. ev.svc: service object. onstop(self, ev): service stop handler. ev.svc: service object. onupdate(self, ev): service per-frame update handler. ev.svc: service object. onidle(self, ev): service per-frame idle handler. ev.svc: service object. ev.idletime: idle time, float type, in seconds. onsessioncreate(self, ev): session create handler. ev.svc: service object. ev.islisten: is listen session or not. ev.session_id: session Id. ev.local_ip: local ip address. ev.local_port: local port number. ev.peer_ip: peer ip address. ev.peer_port: peer port number. onsessiondestroy(self, ev): session destroy handler. ev.svc: service object. ev.session_id: session Id. ev.reason: destroy reason. ev.destroyed_from_service: destroyed from service flag. ev.local_ip: local ip address. ev.local_port: local port number. ev.peer_ip: peer ip address. ev.peer_port: peer port number. ev.socket: socket file descripter. ev.errno: error number(only available when ev.destroyed_from_service is True). ev.sub_errno: sub error number(only available when ev.destroyed_from_service is True). onasyncconnresult(self, ev): async-connect result handler. ev.svc: service object. ev.peer_ip: peer ip address. ev.peer_port: peer port number. ev.connected: connected flag. ev.reason: reason describe. onprotoreport(self, ev): protocol report. ev.svc: service object. ev.report_layer: which layer protocol reported. ev.report_level: report event level(DEBUG, INFO, WARN, ERROR). ev.report_msg: report message. ev.session_id: report session_id(optional, maybe is 0). onunhandledpacket(self, ev): unhandled packet. ev.svc: service object. ev.session_id: packet session Id. ev.opcode: packet opcode. ev.packet: packet object. """ if hasattr(facade, '__bases__') and llbc.ischild(facade, type): raise llbc.error( 'facade could not be type(or derived from type) instance, facade:{}' .format(facade)) elif isinstance(facade, type): raise llbc.error( 'facade could not be class type object, facade: {}'.format( facade)) else: if isinstance(facade, _types.FunctionType): raise llbc.error( 'facade could not be a function, facade: {}'.format( facade)) elif isinstance(facade, _types.MethodType): raise llbc.error( 'facade could not be a method(included bound and unbound), facade: {}' .format(facade)) llbc.inl.RegisterFacade(self._c_obj, facade) self._facades.update({facade.__class__: facade})