210 from cubicweb.appobject import Selector, objectify_selector, lltrace, yes |
210 from cubicweb.appobject import Selector, objectify_selector, lltrace, yes |
211 from cubicweb.schema import split_expression |
211 from cubicweb.schema import split_expression |
212 |
212 |
213 from cubicweb.appobject import traced_selection # XXX for bw compat |
213 from cubicweb.appobject import traced_selection # XXX for bw compat |
214 |
214 |
215 def score_interface(etypesreg, cls_or_inst, cls, iface): |
215 def score_interface(etypesreg, eclass, iface): |
216 """Return XXX if the give object (maybe an instance or class) implements |
216 """Return XXX if the give object (maybe an instance or class) implements |
217 the interface. |
217 the interface. |
218 """ |
218 """ |
219 if getattr(iface, '__registry__', None) == 'etypes': |
219 if getattr(iface, '__registry__', None) == 'etypes': |
220 # adjust score if the interface is an entity class |
220 # adjust score if the interface is an entity class |
221 parents = etypesreg.parent_classes(cls_or_inst.__regid__) |
221 parents, any = etypesreg.parent_classes(eclass.__regid__) |
222 if iface is cls: |
222 if iface is eclass: |
223 return len(parents) + 4 |
223 return len(parents) + 4 |
224 if iface is parents[-1]: # Any |
224 if iface is any: # Any |
225 return 1 |
225 return 1 |
226 for index, basecls in enumerate(reversed(parents[:-1])): |
226 for index, basecls in enumerate(reversed(parents)): |
227 if iface is basecls: |
227 if iface is basecls: |
228 return index + 3 |
228 return index + 3 |
229 return 0 |
229 return 0 |
230 # XXX iface in implements deprecated in 3.9 |
230 # XXX iface in implements deprecated in 3.9 |
231 if implements_iface(cls_or_inst, iface): |
231 if implements_iface(eclass, iface): |
232 # implenting an interface takes precedence other special Any interface |
232 # implementing an interface takes precedence other special Any interface |
233 return 2 |
233 return 2 |
234 return 0 |
234 return 0 |
235 |
235 |
236 |
236 |
237 # abstract selectors / mixin helpers ########################################### |
237 # abstract selectors / mixin helpers ########################################### |
697 def __str__(self): |
697 def __str__(self): |
698 return '%s(%s)' % (self.__class__.__name__, |
698 return '%s(%s)' % (self.__class__.__name__, |
699 ','.join(str(s) for s in self.expected_ifaces)) |
699 ','.join(str(s) for s in self.expected_ifaces)) |
700 |
700 |
701 def score_class(self, eclass, req): |
701 def score_class(self, eclass, req): |
702 return self.score_interfaces(req, eclass, eclass) |
|
703 |
|
704 def score_interfaces(self, req, cls_or_inst, cls): |
|
705 score = 0 |
702 score = 0 |
706 etypesreg = req.vreg['etypes'] |
703 etypesreg = req.vreg['etypes'] |
707 for iface in self.expected_ifaces: |
704 for iface in self.expected_ifaces: |
708 if isinstance(iface, basestring): |
705 if isinstance(iface, basestring): |
709 # entity type |
706 # entity type |
710 try: |
707 try: |
711 iface = etypesreg.etype_class(iface) |
708 iface = etypesreg.etype_class(iface) |
712 except KeyError: |
709 except KeyError: |
713 continue # entity type not in the schema |
710 continue # entity type not in the schema |
714 score += score_interface(etypesreg, cls_or_inst, cls, iface) |
711 score += score_interface(etypesreg, eclass, iface) |
715 return score |
712 return score |
716 |
713 |
717 def _reset_is_instance_cache(vreg): |
714 def _reset_is_instance_cache(vreg): |
718 vreg._is_instance_selector_cache = {} |
715 vreg._is_instance_selector_cache = {} |
719 |
716 |
742 def __str__(self): |
739 def __str__(self): |
743 return '%s(%s)' % (self.__class__.__name__, |
740 return '%s(%s)' % (self.__class__.__name__, |
744 ','.join(str(s) for s in self.expected_etypes)) |
741 ','.join(str(s) for s in self.expected_etypes)) |
745 |
742 |
746 def score_class(self, eclass, req): |
743 def score_class(self, eclass, req): |
747 return self.score_etypes(req, eclass, eclass) |
|
748 |
|
749 def score_etypes(self, req, cls_or_inst, cls): |
|
750 # cache on vreg to avoid reloading issues |
744 # cache on vreg to avoid reloading issues |
751 cache = req.vreg._is_instance_selector_cache |
745 cache = req.vreg._is_instance_selector_cache |
752 try: |
746 try: |
753 expected_eclasses = cache[self] |
747 expected_eclasses = cache[self] |
754 except KeyError: |
748 except KeyError: |
756 # (entity class, parent classes) |
750 # (entity class, parent classes) |
757 etypesreg = req.vreg['etypes'] |
751 etypesreg = req.vreg['etypes'] |
758 expected_eclasses = cache[self] = [] |
752 expected_eclasses = cache[self] = [] |
759 for etype in self.expected_etypes: |
753 for etype in self.expected_etypes: |
760 try: |
754 try: |
761 expected_eclasses.append( |
755 expected_eclasses.append(etypesreg.etype_class(etype)) |
762 (etypesreg.etype_class(etype), |
|
763 etypesreg.parent_classes(etype)) |
|
764 ) |
|
765 except KeyError: |
756 except KeyError: |
766 continue # entity type not in the schema |
757 continue # entity type not in the schema |
|
758 parents, any = req.vreg['etypes'].parent_classes(eclass.__regid__) |
767 score = 0 |
759 score = 0 |
768 for iface, parents in expected_eclasses: |
760 for expectedcls in expected_eclasses: |
769 # adjust score according to class proximity |
761 # adjust score according to class proximity |
770 if iface is cls: |
762 if expectedcls is eclass: |
771 score += len(parents) + 4 |
763 score += len(parents) + 4 |
772 elif iface is parents[-1]: # Any |
764 elif expectedcls is any: # Any |
773 score += 1 |
765 score += 1 |
774 else: |
766 else: |
775 for index, basecls in enumerate(reversed(parents[:-1])): |
767 for index, basecls in enumerate(reversed(parents)): |
776 if iface is basecls: |
768 if expectedcls is basecls: |
777 score += index + 3 |
769 score += index + 3 |
778 break |
770 break |
779 return score |
771 return score |
780 |
772 |
781 |
773 |