diff --git a/python_actr/errors/__init__.py b/python_actr/errors/__init__.py new file mode 100644 index 0000000..0179aa8 --- /dev/null +++ b/python_actr/errors/__init__.py @@ -0,0 +1 @@ +from .errors import * diff --git a/python_actr/errors/errors.py b/python_actr/errors/errors.py new file mode 100644 index 0000000..90d6535 --- /dev/null +++ b/python_actr/errors/errors.py @@ -0,0 +1,10 @@ +class SchedulerException(Exception): + pass + + +class ProductionException(Exception): + pass + + +class PatternException(Exception): + pass diff --git a/python_actr/model.py b/python_actr/model.py index f45f80c..8f6109a 100644 --- a/python_actr/model.py +++ b/python_actr/model.py @@ -1,266 +1,223 @@ - - -from . import scheduler +from . import scheduler from . import logger import random -import inspect +import inspect import copy -#import ccm.config as config + class MethodWrapper: - def __init__(self,obj,func,name): - self.func=func - self.obj=obj - self.__name__=name - self.begins=scheduler.Trigger(name+' begin') - self.ends=scheduler.Trigger(name+' end') - self.default_trigger=self.ends - def __call__(self,*args,**keys): - self.obj.sch.trigger(self.begins) - val=self.func(self.obj,*args,**keys) - self.obj.sch.trigger(self.ends) - return val + + def __init__(self, obj, func, name): + self.func = func + self.obj = obj + self.__name__ = name + self.begins = scheduler.Trigger(f'{name} begin') + self.ends = scheduler.Trigger(f'{name} end') + self.default_trigger = self.ends + + def __call__(self, *args, **keys): + self.obj.sch.trigger(self.begins) + val = self.func(self.obj, *args, **keys) + self.obj.sch.trigger(self.ends) + return val + + class MethodGeneratorWrapper(MethodWrapper): - def _generator(self,*args,**keys): - self.obj.sch.trigger(self.begins) - for x in self.func(self.obj,*args,**keys): - yield x - self.obj.sch.trigger(self.ends) - def __call__(self,*args,**keys): - return self.obj.sch.add(self._generator,args=args,keys=keys) - def __str__(self): - return ''%(self.obj,self.__name__) - -def log_everything(model,log=None): - if log is None: log=logger.log_proxy - if not hasattr(model,'log'): model.run(limit=0) - model.log=log - for k,v in list(model.__dict__.items()): - if k[0]!='_' and k!='parent': - - if isinstance(v,Model) and v.parent is model: - log_everything(v,getattr(log,k)) + + def _generator(self, *args, **keys): + self.obj.sch.trigger(self.begins) + for x in self.func(self.obj, *args, **keys): + yield x + self.obj.sch.trigger(self.ends) + + def __call__(self, *args, **keys): + return self.obj.sch.add(self._generator, args=args, keys=keys) + + def __str__(self): + return f' y. + """ + + return (x > y) - (x < y) -from . import logger class Trigger: - def __init__(self,name=''): - self.name=name + + def __init__(self, name=''): + self.name = name + def __str__(self): - return ''%self.name + return f'' + class Event: - generator=False - def __init__(self,func,time,args=[],keys={},priority=0): - self.name=getattr(func,'func_name',None) - - try: - code=func.__code__ - except AttributeError: - try: - code=func.__call__.__func__.__code__ - except: - code=None - if code and code.co_flags&0x20==0x20: # check for generator - func=func(*args,**keys).__next__ - args=[] - keys={} - self.generator=True - - self.func=func - self.args=args - self.keys=keys - self.time=time - self.priority=priority - self.group=() - self.cancelled=False - self.parent=None - #def __cmp__(self,other): - #return cmp((self.time,-self.priority),(other.time,-other.priority)) - - def __lt__(self,other): - #print(self.time,-self.priority,'other',other.time,-other.priority) - return (self.time,-self.priority) < (other.time,-other.priority) - #return cmp((self.time,-self.priority),(other.time,-other.priority)) - - def __le__(self, other): - return cmp((self.time,-self.priority),(other.time,-other.priority)) - - def __eq__(self,other): - return cmp((self.time,-self.priority),(other.time,-other.priority)) - - def __ge__(self, other): - return cmp((self.time,-self.priority),(other.time,-other.priority)) - - def __gt__(self,other): - return cmp((self.time,-self.priority),(other.time,-other.priority)) - - def __ne__(self, other): - return cmp((self.time,-self.priority),(other.time,-other.priority)) - - def __repr__(self): - return '<%s %x %5.3f>'%(self.name,id(self.func),self.time) - -class SchedulerError(Exception): - pass + + generator = False + + def __init__(self, func, time, args=[], keys={}, priority=0): + self.name = getattr(func, 'func_name', None) + try: + code = func.__code__ + except AttributeError: + try: + code = func.__call__.__func__.__code__ + except AttributeError: + code = None + if code and code.co_flags & 0x20 == 0x20: # check for generator + func = func(*args, **keys).__next__ + args = [] + keys = {} + self.generator = True + + self.func = func + self.args = args + self.keys = keys + self.time = time + self.priority = priority + self.group = () + self.cancelled = False + self.parent = None + + def __lt__(self, other): + return (self.time, -self.priority) < (other.time, -other.priority) + + def __le__(self, other): + return cmp((self.time, -self.priority), (other.time, -other.priority)) + + def __eq__(self, other): + return cmp((self.time, -self.priority), (other.time, -other.priority)) + + def __ge__(self, other): + return cmp((self.time, -self.priority), (other.time, -other.priority)) + + def __gt__(self, other): + return cmp((self.time, -self.priority), (other.time, -other.priority)) + + def __ne__(self, other): + return cmp((self.time, -self.priority), (other.time, -other.priority)) + + def __repr__(self): + return f'<{self.name} {id(self.func)} {self.time:.3f}>' + class Scheduler: + def __init__(self): - self.queue=[] - self.to_be_added=[] - self.triggers={} - self.time=0.0 - self.stop_flag=False - self.log=logger.log_proxy - def extend(self,other): - for k,v in list(other.triggers.items()): + self.queue = [] + self.to_be_added = [] + self.triggers = {} + self.time = 0.0 + self.stop_flag = False + self.log = logger.log_proxy + + def extend(self, other): + for k, v in list(other.triggers.items()): if k not in self.triggers: - self.triggers[k]=v + self.triggers[k] = v else: self.triggers[k].extend(v) - if len(other.queue)>0: + if len(other.queue) > 0: self.queue.extend(other.queue) - heapq.heapify(self.queue) - def trigger(self,key,priority=None): + heapq.heapify(self.queue) + + def trigger(self, key, priority=None): if 'OpenGL' in key.name: - print(key.name) - print(key in self.triggers) + print(key.name) + print(key in self.triggers) if key in self.triggers: for event in self.triggers[key]: - event.time=self.time + event.time = self.time if priority is not None: - event.priority=priority + event.priority = priority self.add_event(event) del self.triggers[key][:] - def add_event(self,event): - heapq.heappush(self.queue,event) - def add(self,func,delay=0,args=[],keys={},priority=0,thread_safe=False): + + def add_event(self, event): + heapq.heappush(self.queue, event) + + def add(self, func, delay=0, + args=[], keys={}, priority=0, thread_safe=False): if thread_safe: - self.to_be_added.append((func,delay,args,keys,priority)) - else: - ev=Event(func,self.time+delay,args=args,keys=keys,priority=priority) - self.add_event(ev) - return ev - + self.to_be_added.append((func, delay, args, keys, priority)) + else: + ev = Event(func, self.time+delay, args=args, + keys=keys, priority=priority) + self.add_event(ev) + return ev + def run(self): - self.stop_flag=False - while not self.stop_flag and len(self.queue)>0: - next=self.queue[0].time - if next>self.time: - self.time=next - self.log.time=next - self.do_event(heapq.heappop(self.queue)) + self.stop_flag = False + + while not self.stop_flag and len(self.queue) > 0: + next = self.queue[0].time + if next > self.time: + self.time = next + self.log.time = next + self.do_event(heapq.heappop(self.queue)) + while self.to_be_added: - self.add(*self.to_be_added.pop()) - - def handle_result(self,result,event): - if isinstance(result,(int,float)): - event.time=self.time+result + self.add(*self.to_be_added.pop()) + + def handle_result(self, result, event): + if isinstance(result, (int, float)): + event.time = self.time + result self.add_event(event) - elif isinstance(result,dict): - event.time=self.time+result.get('delay',0) - event.priority=result.get('priority',event.priority) + elif isinstance(result, dict): + event.time = self.time + result.get('delay', 0) + event.priority = result.get('priority', event.priority) self.add_event(event) - elif isinstance(result,(str,Trigger)): - event.time=None + elif isinstance(result, (str, Trigger)): + event.time = None if result not in self.triggers: - self.triggers[result]=[event] + self.triggers[result] = [event] else: self.triggers[result].append(event) - elif isinstance(result,(list,tuple)): - events=[copy.copy(event) for r in result] - for e in events: e.group=events - for i,r in enumerate(result): - self.handle_result(r,events[i]) + elif isinstance(result, (list, tuple)): + events = [copy.copy(event) for r in result] + for e in events: + e.group = events + for i, r in enumerate(result): + self.handle_result(r, events[i]) elif result is None: if event.parent is not None: - event.parent.time=self.time + event.parent.time = self.time self.add_event(event.parent) - elif isinstance(result,Event): + elif isinstance(result, Event): if result.generator and event.generator: - result.parent=event - elif hasattr(result,'default_trigger'): - self.handle_result(result.default_trigger,event) + result.parent = event + elif hasattr(result, 'default_trigger'): + self.handle_result(result.default_trigger, event) else: - raise SchedulerError("Incorrect 'yield': %s"%(result)) - - - - def do_event(self,event): - assert self.time==event.time + raise SchedulerException(f"Incorrect 'yield': {result}") + + def do_event(self, event): + assert self.time == event.time - if event.cancelled: return - for e in event.group: e.cancelled=True - event.cancelled=False + if event.cancelled: + return + for e in event.group: + e.cancelled = True + event.cancelled = False try: - result=event.func(*event.args,**event.keys) + result = event.func(*event.args, **event.keys) except StopIteration: - result=None - - self.handle_result(result,event) - - - - - - - - def stop(self): - self.stop_flag=True + result = None + + self.handle_result(result, event) + + def stop(self): + self.stop_flag = True diff --git a/setup.py b/setup.py index 4fd238a..2c93f18 100644 --- a/setup.py +++ b/setup.py @@ -1,10 +1,11 @@ -from setuptools import setup - +from setuptools import setup, find_packages import python_actr +exclude = ['python_actr.tests'] + setup( name='python_actr', - packages=['python_actr', 'python_actr.display', 'python_actr.actr', 'python_actr.ui'], + packages=find_packages(exclude=exclude), version=python_actr.version.version, author='Carleton Cognitive Modelling Lab', description='Python implementation of the ACT-R cognitive architecture',