class Tweens(object): def __init__(self): self.sorter = TopologicalSorter( default_before=None, default_after=INGRESS, first=INGRESS, last=MAIN, ) self.explicit = [] def add_explicit(self, name, factory): self.explicit.append((name, factory)) def add_implicit(self, name, factory, under=None, over=None): self.sorter.add(name, factory, after=under, before=over) def implicit(self): return self.sorter.sorted() def __call__(self, handler, registry): if self.explicit: use = self.explicit else: use = self.implicit() for name, factory in use[::-1]: handler = factory(handler, registry) return handler
class Tweens(object): def __init__(self): self.sorter = TopologicalSorter( default_before=None, default_after=INGRESS, first=INGRESS, last=MAIN) self.explicit = [] def add_explicit(self, name, factory): self.explicit.append((name, factory)) def add_implicit(self, name, factory, under=None, over=None): self.sorter.add(name, factory, after=under, before=over) def implicit(self): return self.sorter.sorted() def __call__(self, handler, registry): if self.explicit: use = self.explicit else: use = self.implicit() for name, factory in use[::-1]: handler = factory(handler, registry) return handler
def __init__(self): self.sorter = TopologicalSorter( default_before=None, default_after=INGRESS, first=INGRESS, last=MAIN, ) self.explicit = []
def get_dumpers(registry): ordered = registry.queryUtility(IDumperFactories, default=None) if ordered is None: tsorter = TopologicalSorter() dumpers = registry.pop('_sd_dumpers', []) for n, f, b, a in dumpers: tsorter.add(n, f, before=b, after=a) ordered = tsorter.sorted() registry.registerUtility(ordered, IDumperFactories) dumpers = [f(n, registry) for n, f in ordered] return dumpers
def __init__(self): self.sorter = TopologicalSorter( default_before=None, default_after=INGRESS, first=INGRESS, last=MAIN) self.explicit = []
def register(): tsorter = config.registry.queryUtility(IEvolutionSteps) if tsorter is None: tsorter = TopologicalSorter() config.registry.registerUtility(tsorter, IEvolutionSteps) tsorter.add(name, (name, func), before=before, after=after)
def _makeOne(self, *arg, **kw): from pyramid.util import TopologicalSorter return TopologicalSorter(*arg, **kw)
def __init__(self): self.sorter = TopologicalSorter() self.last_added = None
class PredicateList(object): def __init__(self): self.sorter = TopologicalSorter() self.last_added = None def add(self, name, factory, weighs_more_than=None, weighs_less_than=None): # Predicates should be added to a predicate list in (presumed) # computation expense order. # if weighs_more_than is None and weighs_less_than is None: # weighs_more_than = self.last_added or FIRST # weighs_less_than = LAST self.last_added = name self.sorter.add( name, factory, after=weighs_more_than, before=weighs_less_than ) def names(self): # Return the list of valid predicate names. return self.sorter.names def make(self, config, **kw): # Given a configurator and a list of keywords, a predicate list is # computed. Elsewhere in the code, we evaluate predicates using a # generator expression. All predicates associated with a view or # route must evaluate true for the view or route to "match" during a # request. The fastest predicate should be evaluated first, then the # next fastest, and so on, as if one returns false, the remainder of # the predicates won't need to be evaluated. # # While we compute predicates, we also compute a predicate hash (aka # phash) that can be used by a caller to identify identical predicate # lists. ordered = self.sorter.sorted() phash = md5() weights = [] preds = [] for n, (name, predicate_factory) in enumerate(ordered): vals = kw.pop(name, None) if vals is None: # XXX should this be a sentinel other than None? continue if not isinstance(vals, predvalseq): vals = (vals,) for val in vals: realval = val notted = False if isinstance(val, not_): realval = val.value notted = True pred = predicate_factory(realval, config) if notted: pred = Notted(pred) hashes = pred.phash() if not is_nonstr_iter(hashes): hashes = [hashes] for h in hashes: phash.update(bytes_(h)) weights.append(1 << n + 1) preds.append(pred) if kw: from difflib import get_close_matches closest = [] names = [name for name, _ in ordered] for name in kw: closest.extend(get_close_matches(name, names, 3)) raise ConfigurationError( 'Unknown predicate values: %r (did you mean %s)' % (kw, ','.join(closest)) ) # A "order" is computed for the predicate list. An order is # a scoring. # # Each predicate is associated with a weight value. The weight of a # predicate symbolizes the relative potential "importance" of the # predicate to all other predicates. A larger weight indicates # greater importance. # # All weights for a given predicate list are bitwise ORed together # to create a "score"; this score is then subtracted from # MAX_ORDER and divided by an integer representing the number of # predicates+1 to determine the order. # # For views, the order represents the ordering in which a "multiview" # ( a collection of views that share the same context/request/name # triad but differ in other ways via predicates) will attempt to call # its set of views. Views with lower orders will be tried first. # The intent is to a) ensure that views with more predicates are # always evaluated before views with fewer predicates and b) to # ensure a stable call ordering of views that share the same number # of predicates. Views which do not have any predicates get an order # of MAX_ORDER, meaning that they will be tried very last. score = 0 for bit in weights: score = score | bit order = (MAX_ORDER - score) / (len(preds) + 1) return order, preds, phash.hexdigest()
class PredicateList(object): def __init__(self): self.sorter = TopologicalSorter() self.last_added = None def add(self, name, factory, weighs_more_than=None, weighs_less_than=None): # Predicates should be added to a predicate list in (presumed) # computation expense order. # if weighs_more_than is None and weighs_less_than is None: # weighs_more_than = self.last_added or FIRST # weighs_less_than = LAST self.last_added = name self.sorter.add(name, factory, after=weighs_more_than, before=weighs_less_than) def names(self): # Return the list of valid predicate names. return self.sorter.names def make(self, config, **kw): # Given a configurator and a list of keywords, a predicate list is # computed. Elsewhere in the code, we evaluate predicates using a # generator expression. All predicates associated with a view or # route must evaluate true for the view or route to "match" during a # request. The fastest predicate should be evaluated first, then the # next fastest, and so on, as if one returns false, the remainder of # the predicates won't need to be evaluated. # # While we compute predicates, we also compute a predicate hash (aka # phash) that can be used by a caller to identify identical predicate # lists. ordered = self.sorter.sorted() phash = md5() weights = [] preds = [] for n, (name, predicate_factory) in enumerate(ordered): vals = kw.pop(name, None) if vals is None: # XXX should this be a sentinel other than None? continue if not isinstance(vals, predvalseq): vals = (vals, ) for val in vals: realval = val notted = False if isinstance(val, not_): realval = val.value notted = True pred = predicate_factory(realval, config) if notted: pred = Notted(pred) hashes = pred.phash() if not is_nonstr_iter(hashes): hashes = [hashes] for h in hashes: phash.update(bytes_(h)) weights.append(1 << n + 1) preds.append(pred) if kw: from difflib import get_close_matches closest = [] names = [name for name, _ in ordered] for name in kw: closest.extend(get_close_matches(name, names, 3)) raise ConfigurationError( 'Unknown predicate values: %r (did you mean %s)' % (kw, ','.join(closest))) # A "order" is computed for the predicate list. An order is # a scoring. # # Each predicate is associated with a weight value. The weight of a # predicate symbolizes the relative potential "importance" of the # predicate to all other predicates. A larger weight indicates # greater importance. # # All weights for a given predicate list are bitwise ORed together # to create a "score"; this score is then subtracted from # MAX_ORDER and divided by an integer representing the number of # predicates+1 to determine the order. # # For views, the order represents the ordering in which a "multiview" # ( a collection of views that share the same context/request/name # triad but differ in other ways via predicates) will attempt to call # its set of views. Views with lower orders will be tried first. # The intent is to a) ensure that views with more predicates are # always evaluated before views with fewer predicates and b) to # ensure a stable call ordering of views that share the same number # of predicates. Views which do not have any predicates get an order # of MAX_ORDER, meaning that they will be tried very last. score = 0 for bit in weights: score = score | bit order = (MAX_ORDER - score) / (len(preds) + 1) return order, preds, phash.hexdigest()
def sdi_mgmt_views(context, request, names=None): if not hasattr(context, '__name__'): # shortcut if the context doesn't have a name (usually happens if the # context is an exception); we do this because mgmt_path uses Pyramid's # resource_path_tuple, which wants every object in the lineage to have # a __name__. return [] registry = request.registry introspector = registry.introspector unordered = [] # create a dummy request signaling our intent req = Request(request.environ.copy()) req.script_name = request.script_name req.context = context req.matched_route = request.matched_route req.method = 'GET' req.registry = request.registry for data in introspector.get_category('sdi views'): related = data['related'] sdi_intr = data['introspectable'] tab_title = sdi_intr['tab_title'] tab_condition = sdi_intr['tab_condition'] tab_before = sdi_intr['tab_before'] tab_after = sdi_intr['tab_after'] def is_view(intr): return intr.category_name == 'views' for view_intr in filter(is_view, related): # NB: in reality, the above loop will execute exactly once because # each "sdi view" is associated with exactly one pyramid view view_name = view_intr['name'] req.path_info = request.mgmt_path(context, view_name) if names is not None and not view_name in names: continue # do a passable job at figuring out whether, if we visit the # url implied by this view, we'll be permitted to view it and # something reasonable will show up intr_context = view_intr['context'] if IInterface.providedBy(intr_context): if not intr_context.providedBy(context): continue elif intr_context and not isinstance(context, intr_context): continue if tab_condition is not None and names is None: if callable(tab_condition): if not tab_condition(context, request): continue elif not tab_condition: continue derived = view_intr['derived_callable'] if hasattr(derived, '__predicated__'): if not derived.__predicated__(context, req): continue if hasattr(derived, '__permitted__'): if not derived.__permitted__(context, req): continue if view_name == request.view_name: css_class = 'active' else: css_class = None unordered.append( {'view_name': view_name, 'tab_before':tab_before, 'tab_after':tab_after, 'title': tab_title or view_name.capitalize(), 'class': css_class, 'url': request.mgmt_path(request.context, '@@%s' % view_name) } ) manually_ordered = [] tab_order = request.registry.content.metadata(context, 'tab_order') if tab_order is not None: ordered_names = [ y for y in tab_order if y in [ x['view_name'] for x in unordered ] ] for ordered_name in ordered_names: for view_data in unordered[:]: if view_data['view_name'] == ordered_name: unordered.remove(view_data) manually_ordered.append(view_data) # first sort non-manually-ordered views lexically by title lexically_ordered = sorted(unordered, key=operator.itemgetter('title')) # then sort the lexically-presorted unordered views topologically based on # any tab_before and tab_after values in the view data tsorter = TopologicalSorter(default_before=MIDDLE, default_after=None) tsorter.add( MIDDLE, None, before=LAST, after=FIRST, ) for view_data in lexically_ordered: before=view_data.get('tab_before', None) after=view_data.get('tab_after', None) tsorter.add( view_data['view_name'], view_data, before=before, after=after, ) topo_ordered = [ x[1] for x in tsorter.sorted() if x[0] is not MIDDLE ] return manually_ordered + topo_ordered
def sdi_mgmt_views(context, request, names=None): if not hasattr(context, '__name__'): # shortcut if the context doesn't have a name (usually happens if the # context is an exception); we do this because mgmt_path uses Pyramid's # resource_path_tuple, which wants every object in the lineage to have # a __name__. return [] registry = request.registry introspector = registry.introspector unordered = [] # create a dummy request signaling our intent req = Request(request.environ.copy()) req.script_name = request.script_name req.context = context req.matched_route = request.matched_route req.method = 'GET' req.registry = request.registry sro_enum = list(enumerate(providedBy(context).__sro__[:-1])) for data in introspector.get_category('sdi views'): related = data['related'] sdi_intr = data['introspectable'] tab_title = sdi_intr['tab_title'] tab_condition = sdi_intr['tab_condition'] tab_before = sdi_intr['tab_before'] tab_after = sdi_intr['tab_after'] def is_view(intr): return intr.category_name == 'views' for view_intr in filter(is_view, related): # NB: in reality, the above loop will execute exactly once because # each "sdi view" is associated with exactly one pyramid view view_name = view_intr['name'] req.path_info = request.sdiapi.mgmt_path(context, view_name) if names is not None and not view_name in names: continue # do a passable job at figuring out whether, if we visit the # url implied by this view, we'll be permitted to view it and # something reasonable will show up intr_context = view_intr['context'] sro_index = MAX_ORDER if intr_context is None: intr_context = Interface if IInterface.providedBy(intr_context): if not intr_context.providedBy(context): continue for i, spec in sro_enum: if spec is intr_context: sro_index = i break elif isinstance(context, intr_context): for i, spec in sro_enum: if spec.implementedBy(intr_context): sro_index = i break else: # pragma: no cover (coverage bug, this is reached) continue if tab_condition is not None and names is None: if callable(tab_condition): if not tab_condition(context, request): continue elif not tab_condition: continue derived = view_intr['derived_callable'] if hasattr(derived, '__predicated__'): if not derived.__predicated__(context, req): continue if hasattr(derived, '__permitted__'): if not derived.__permitted__(context, req): continue predicate_order = getattr(derived, '__order__', MAX_ORDER) if view_name == request.view_name: css_class = 'active' else: css_class = None unordered.append({ 'view_name': view_name, 'tab_before': tab_before, 'tab_after': tab_after, 'title': tab_title or view_name.capitalize(), 'class': css_class, 'predicate_order': predicate_order, 'sro_index': sro_index, 'url': request.sdiapi.mgmt_path(request.context, '@@%s' % view_name) }) # De-duplicate the unordered list of tabs with the same view_name. Prefer # the tab with the lowest (sro_index, predicate_order) tuple, because this # is the view that's most likely to be executed when visited and we'd # like to get its title right. unordered.sort(key=lambda s: (s['sro_index'], s['predicate_order'])) deduplicated = [] view_names = {} # use a sort-break to take only the first of each same-named view data. for view_data in unordered: vn = view_data['view_name'] if vn in view_names: continue view_names[vn] = True deduplicated.append(view_data) manually_ordered = [] tab_order = request.registry.content.metadata(context, 'tab_order') if tab_order is not None: ordered_names = [ y for y in tab_order if y in [x['view_name'] for x in deduplicated] ] for ordered_name in ordered_names: for view_data in unordered[:]: if view_data['view_name'] == ordered_name: deduplicated.remove(view_data) manually_ordered.append(view_data) # Sort non-manually-ordered views lexically by title. Reverse due to the # behavior of the toposorter; we'd like groups of things that share the # same before/after to be alpha sorted ascending relative to each other, # and reversing lexical ordering here gets us that behavior down the line. lexically_ordered = sorted( deduplicated, key=operator.itemgetter('title'), reverse=True, ) # Sort the lexically-presorted unordered views topologically based on any # tab_before and tab_after values in the view data. tsorter = TopologicalSorter(default_after=CENTER1, default_before=CENTER2) tsorter.add( CENTER1, None, after=FIRST, before=CENTER2, ) tsorter.add( CENTER2, None, after=CENTER1, before=LAST, ) for view_data in lexically_ordered: before = view_data.get('tab_before', None) after = view_data.get('tab_after', None) tsorter.add( view_data['view_name'], view_data, before=before, after=after, ) topo_ordered = [ x[1] for x in tsorter.sorted() if x[0] not in (CENTER1, CENTER2) ] return manually_ordered + topo_ordered
def sdi_mgmt_views(context, request, names=None): if not hasattr(context, '__name__'): # shortcut if the context doesn't have a name (usually happens if the # context is an exception); we do this because mgmt_path uses Pyramid's # resource_path_tuple, which wants every object in the lineage to have # a __name__. return [] registry = request.registry introspector = registry.introspector unordered = [] # create a dummy request signaling our intent req = Request(request.environ.copy()) req.script_name = request.script_name req.context = context req.matched_route = request.matched_route req.method = 'GET' req.registry = request.registry sro_enum = list(enumerate(providedBy(context).__sro__[:-1])) for data in introspector.get_category('sdi views'): related = data['related'] sdi_intr = data['introspectable'] tab_title = sdi_intr['tab_title'] tab_condition = sdi_intr['tab_condition'] tab_before = sdi_intr['tab_before'] tab_after = sdi_intr['tab_after'] def is_view(intr): return intr.category_name == 'views' for view_intr in filter(is_view, related): # NB: in reality, the above loop will execute exactly once because # each "sdi view" is associated with exactly one pyramid view view_name = view_intr['name'] req.path_info = request.sdiapi.mgmt_path(context, view_name) if names is not None and not view_name in names: continue # do a passable job at figuring out whether, if we visit the # url implied by this view, we'll be permitted to view it and # something reasonable will show up intr_context = view_intr['context'] sro_index = MAX_ORDER if intr_context is None: intr_context = Interface if IInterface.providedBy(intr_context): if not intr_context.providedBy(context): continue for i, spec in sro_enum: if spec is intr_context: sro_index = i break elif isinstance(context, intr_context): for i, spec in sro_enum: if spec.implementedBy(intr_context): sro_index = i break else: # pragma: no cover (coverage bug, this is reached) continue if tab_condition is not None and names is None: if callable(tab_condition): if not tab_condition(context, request): continue elif not tab_condition: continue derived = view_intr['derived_callable'] if hasattr(derived, '__predicated__'): if not derived.__predicated__(context, req): continue if hasattr(derived, '__permitted__'): if not derived.__permitted__(context, req): continue predicate_order = getattr(derived, '__order__', MAX_ORDER) if view_name == request.view_name: css_class = 'active' else: css_class = None unordered.append( {'view_name': view_name, 'tab_before':tab_before, 'tab_after':tab_after, 'title': tab_title or view_name.capitalize(), 'class': css_class, 'predicate_order':predicate_order, 'sro_index':sro_index, 'url': request.sdiapi.mgmt_path( request.context, '@@%s' % view_name) } ) # De-duplicate the unordered list of tabs with the same view_name. Prefer # the tab with the lowest (sro_index, predicate_order) tuple, because this # is the view that's most likely to be executed when visited and we'd # like to get its title right. unordered.sort(key=lambda s: (s['sro_index'], s['predicate_order'])) deduplicated = [] view_names = {} # use a sort-break to take only the first of each same-named view data. for view_data in unordered: vn = view_data['view_name'] if vn in view_names: continue view_names[vn] = True deduplicated.append(view_data) manually_ordered = [] tab_order = request.registry.content.metadata(context, 'tab_order') if tab_order is not None: ordered_names = [ y for y in tab_order if y in [ x['view_name'] for x in deduplicated ] ] for ordered_name in ordered_names: for view_data in unordered[:]: if view_data['view_name'] == ordered_name: deduplicated.remove(view_data) manually_ordered.append(view_data) # Sort non-manually-ordered views lexically by title. Reverse due to the # behavior of the toposorter; we'd like groups of things that share the # same before/after to be alpha sorted ascending relative to each other, # and reversing lexical ordering here gets us that behavior down the line. lexically_ordered = sorted( deduplicated, key=operator.itemgetter('title'), reverse=True, ) # Sort the lexically-presorted unordered views topologically based on any # tab_before and tab_after values in the view data. tsorter = TopologicalSorter(default_after=CENTER1, default_before=CENTER2) tsorter.add( CENTER1, None, after=FIRST, before=CENTER2, ) tsorter.add( CENTER2, None, after=CENTER1, before=LAST, ) for view_data in lexically_ordered: before=view_data.get('tab_before', None) after=view_data.get('tab_after', None) tsorter.add( view_data['view_name'], view_data, before=before, after=after, ) topo_ordered = [ x[1] for x in tsorter.sorted() if x[0] not in (CENTER1, CENTER2) ] return manually_ordered + topo_ordered
def includeme(config): config.registry.registerUtility(TopologicalSorter(), ITaskDerivers) config.add_directive( "add_task_deriver", add_task_deriver, action_wrap=True )
def sdi_mgmt_views(context, request, names=None): if not hasattr(context, '__name__'): # shortcut if the context doesn't have a name (usually happens if the # context is an exception); we do this because mgmt_path uses Pyramid's # resource_path_tuple, which wants every object in the lineage to have # a __name__. return [] registry = request.registry introspector = registry.introspector unordered = [] # create a dummy request signaling our intent req = Request(request.environ.copy()) req.script_name = request.script_name req.context = context req.matched_route = request.matched_route req.method = 'GET' req.registry = request.registry for data in introspector.get_category('sdi views'): related = data['related'] sdi_intr = data['introspectable'] tab_title = sdi_intr['tab_title'] tab_condition = sdi_intr['tab_condition'] tab_before = sdi_intr['tab_before'] tab_after = sdi_intr['tab_after'] def is_view(intr): return intr.category_name == 'views' for view_intr in filter(is_view, related): # NB: in reality, the above loop will execute exactly once because # each "sdi view" is associated with exactly one pyramid view view_name = view_intr['name'] req.path_info = request.mgmt_path(context, view_name) if names is not None and not view_name in names: continue # do a passable job at figuring out whether, if we visit the # url implied by this view, we'll be permitted to view it and # something reasonable will show up intr_context = view_intr['context'] if IInterface.providedBy(intr_context): if not intr_context.providedBy(context): continue elif intr_context and not isinstance(context, intr_context): continue if tab_condition is not None and names is None: if callable(tab_condition): if not tab_condition(context, request): continue elif not tab_condition: continue derived = view_intr['derived_callable'] if hasattr(derived, '__predicated__'): if not derived.__predicated__(context, req): continue if hasattr(derived, '__permitted__'): if not derived.__permitted__(context, req): continue if view_name == request.view_name: css_class = 'active' else: css_class = None unordered.append({ 'view_name': view_name, 'tab_before': tab_before, 'tab_after': tab_after, 'title': tab_title or view_name.capitalize(), 'class': css_class, 'url': request.mgmt_path(request.context, '@@%s' % view_name) }) manually_ordered = [] tab_order = request.registry.content.metadata(context, 'tab_order') if tab_order is not None: ordered_names = [ y for y in tab_order if y in [x['view_name'] for x in unordered] ] for ordered_name in ordered_names: for view_data in unordered[:]: if view_data['view_name'] == ordered_name: unordered.remove(view_data) manually_ordered.append(view_data) # first sort non-manually-ordered views lexically by title lexically_ordered = sorted(unordered, key=operator.itemgetter('title')) # then sort the lexically-presorted unordered views topologically based on # any tab_before and tab_after values in the view data tsorter = TopologicalSorter(default_before=MIDDLE, default_after=None) tsorter.add( MIDDLE, None, before=LAST, after=FIRST, ) for view_data in lexically_ordered: before = view_data.get('tab_before', None) after = view_data.get('tab_after', None) tsorter.add( view_data['view_name'], view_data, before=before, after=after, ) topo_ordered = [x[1] for x in tsorter.sorted() if x[0] is not MIDDLE] return manually_ordered + topo_ordered