[docs]classApplyMixin:"""A mixin for predicate-based filters providing the `apply` method. Most filters are predicate-based in the sense that they simply filter targets one by one based on a predicate function that decides whether to include the target or not. Such filters can inherit this mixin and then only need to declare a :func:`predicate` method which returns `True` if the target should be included and `False` otherwise. For examples of using this mixin, see the :class:`StatusFilter` and :class:`EndpointFilter` filters. """
[docs]defapply(self,targets):"""Apply the filter to all `targets`. This method returns a generator yielding all targets in `targets` for each :func:`predicate` returns `True`. """return(targetfortargetintargetsifself.predicate(target))
[docs]defpredicate(self,target):"""Return `True` if `target` should be included, `False` otherwise. This method must be overriden by subclasses. """raiseNotImplementedError("predicate() must be implemented by subclasses.")
classStatusFilter(ApplyMixin):def__init__(self,status_provider,status):self.status_provider=status_providerifnothasattr(status,"__iter__"):status=[status]self.status=statusdefpredicate(self,target):returnself.status_provider(target)inself.statusclassNameFilter:def__init__(self,patterns):self.patterns=patternsdefapply(self,targets):target_name_map={target.name:targetfortargetintargets}return{target_name_map[name]forpatterninself.patternsfornameinfnmatch.filter(target_name_map.keys(),pattern)}classGroupFilter(ApplyMixin):def__init__(self,patterns):self.patterns=patternsdefpredicate(self,target):returnany(fnmatch.filter([target.groupor"none"],p)forpinself.patterns)classEndpointFilter(ApplyMixin):def__init__(self,endpoints,mode="include"):self.endpoints=endpointsself.mode=modedefpredicate(self,target):ifself.mode=="exclude":returntargetnotinself.endpointselifself.mode=="include":returntargetinself.endpointselse:raiseValueError('Argument mode must be either "include" or "exclude".')classCompositeFilter:def__init__(self,filters=None):self.filters=filtersdefapply(self,targets):forfilterinself.filters:targets=filter.apply(targets)returntargets
[docs]deffilter_generic(targets,filters):"""Filter targets given a list of filters. Return all targets from `targets` passing all `filters`. For example: .. code-block:: python matched_targets = filter_generic( targets=graph.targets.values(), filters=[ NameFilter(patterns=['Foo*'], StatusFilter(scheduler=scheduler, status='running'), ] ) returns a generator yielding all targets with a name matching ``Foo*`` which are currently running. :arg targets: A list of targets to be filtered. :arg filters: A list of :class:`Filter` instances. """returnCompositeFilter(filters).apply(targets)
[docs]deffilter_names(targets,patterns):"""Filter targets with a list of patterns. Return all targets in `targets` where the target name matches one or more of the patterns in `pattern`. For example: .. code-block:: python matched_targets = filter_names(graph.targets.values(), ['Foo*']) returns a generator yielding all targets with a name matching the pattern `Foo*`. Multiple patterns can be provided: .. code-block:: python matched_targets = filter_names(graph.targets.values(), ['Foo*', 'Bar*']) returns all targets with a name matching either ``Foo*`` or ``Bar*``. This function is a simple wrapper around :class:`NameFilter`. """returnNameFilter(patterns=patterns).apply(targets)