<commit_before>#!/usr/bin/env python

"""Perform multiple method calls, determined at runtime, on independent items

Construct arbitrarily complex workflows in which the specific methods run are
determined at runtime. These methods are applied to items that are assumed to
be independent.

As an example:

class MyWorkflow(Workflow):
    def _allocate_final_state(self):
        self.FinalState = None

    def _sanity_check(self):
        pass

    @priority(100)
    @no_requirements
    def wf_mul(self, item):
        self.FinalState *= item

    @priority(10)
    @requires(Option='add_value')
    def wf_add(self, item):
        self.FinalState += item

    @requires(Option='sub_value', Values=[1,5,10])
    def wf_sub(self, item):
        self.FinalState -= item
        self.FinalState -= self.Options['sub_value']

    @priority(1000)
    @requires(IsValid=False)
    def wf_init(self, item):
        self.FinalState = item

# (i * i) + i - i - 5
wf = MyWorkflow(Options={'add_value':None, 'sub_value':5})
gen = (i for i in range(10))
for i in wf(gen):
    print i

# (i * i) - i - 10
wf = MyWorkflow(Options={'sub_value':10})
gen = (i for i in range(10))
for i in wf(gen):
    print i

# (i * i)
wf = MyWorkflow()
gen = (i for i in range(10))
for i in wf(gen):
    print i
"""

from itertools import chain
from functools import update_wrapper
from collections import Iterable, defaultdict
from types import MethodType

# thank you Flask project...
_missing = object()   # internal, represents a missing value
_executed = object()  # internal, tag for an executed method
not_none = object()   # external, for when a value can be anything except None


class Exists(object):
    def __contains__(self, item):
        return True
option_exists = Exists()


def _debug_trace_wrapper(obj, f):
    """Trace a function call"""
    def wrapped(self, *args, **kwargs):
        if not hasattr(obj, 'DebugTrace'):
            raise AttributeError("%s doesn't have DebugTrace!" % obj.__class__)

        obj.DebugTrace.append(f.__name__)
        return f(self, *args, **kwargs)

    return update_wrapper(wrapped, f)


def _tag_function(f):
    """Tag, you're it"""
    setattr(f, '__workflowtag__', None)


class priority(object):
    """Sets a function priority"""
    def __init__(self, Priority):
        self.Priority = Priority

    def __call__(self, f):
        f.Priority = self.Priority
        return f


def no_requirements(f):
    def decorated(self, *args, **kwargs):
        f(self, *args, **kwargs)
        return _executed
    _tag_function(decorated)
    return update_wrapper(decorated, f)


class requires(object):
    """Decorator that executes a function if requirements are met"""
    def __init__(self, IsValid=True, Option=None, Values=_missing,
                 ValidData=None):
        """
        IsValid : execute the function if self.Failed is False
        Option : a required option
        Values : required values associated with an option
        ValidData : data level requirements, this must be a function with the
            following signature: f(*args, **kwargs) returning True. NOTE: if
            ValidData returns False on the first item evaluated, the decorated
            function will be removed from the remaining workflow.
        """
        # self here is the requires object
        self.IsValid = IsValid
        self.Option = Option
        self.ValidData = ValidData

        if Values is _missing:
            self.Values = option_exists
        elif Values is not_none:
            self.Values = not_none
        elif not isinstance(Values, set):
            if isinstance(Values, str):
                self.Values = Values
            elif isinstance(Values, Iterable):
                self.Values = set(Values)
            else:
                self.Values = set([Values])
        else:
            self.Values = Values

    def doShortCircuit(self, wrapped):
        if self.IsValid and (wrapped.Failed and wrapped.ShortCircuit):
            return True
        else:
            return False

    def __call__(self, f):
        """Wrap a function

        f : the function to wrap
        """
        ### not sure how I feel about having multiple functions in here.
        ### also, the handling of Data is a bit dirty as it is now replicated
        ### over these functions. It is ideal to keep the functions slim, thus<commit_msg>removed _missing, added anything<commit_after>#!/usr/bin/env python

"""Perform multiple method calls, determined at runtime, on independent items

Construct arbitrarily complex workflows in which the specific methods run are
determined at runtime. These methods are applied to items that are assumed to
be independent.

As an example:

class MyWorkflow(Workflow):
    def _allocate_final_state(self):
        self.FinalState = None

    def _sanity_check(self):
        pass

    @priority(100)
    @no_requirements
    def wf_mul(self, item):
        self.FinalState *= item

    @priority(10)
    @requires(Option='add_value')
    def wf_add(self, item):
        self.FinalState += item

    @requires(Option='sub_value', Values=[1,5,10])
    def wf_sub(self, item):
        self.FinalState -= item
        self.FinalState -= self.Options['sub_value']

    @priority(1000)
    @requires(IsValid=False)
    def wf_init(self, item):
        self.FinalState = item

# (i * i) + i - i - 5
wf = MyWorkflow(Options={'add_value':None, 'sub_value':5})
gen = (i for i in range(10))
for i in wf(gen):
    print i

# (i * i) - i - 10
wf = MyWorkflow(Options={'sub_value':10})
gen = (i for i in range(10))
for i in wf(gen):
    print i

# (i * i)
wf = MyWorkflow()
gen = (i for i in range(10))
for i in wf(gen):
    print i
"""

from itertools import chain
from functools import update_wrapper
from collections import Iterable, defaultdict
from types import MethodType

# thank you Flask project...
_executed = object()  # internal, tag for an executed method
not_none = object()   # external, for when a value can be anything except None


class Exists(object):
    def __contains__(self, item):
        return True
anything = Exists()  # external, for when a value can be anything


def _debug_trace_wrapper(obj, f):
    """Trace a function call"""
    def wrapped(self, *args, **kwargs):
        if not hasattr(obj, 'DebugTrace'):
            raise AttributeError("%s doesn't have DebugTrace!" % obj.__class__)

        obj.DebugTrace.append(f.__name__)
        return f(self, *args, **kwargs)

    return update_wrapper(wrapped, f)


def _tag_function(f):
    """Tag, you're it"""
    setattr(f, '__workflowtag__', None)


class priority(object):
    """Sets a function priority"""
    def __init__(self, Priority):
        self.Priority = Priority

    def __call__(self, f):
        f.Priority = self.Priority
        return f


def no_requirements(f):
    def decorated(self, *args, **kwargs):
        f(self, *args, **kwargs)
        return _executed
    _tag_function(decorated)
    return update_wrapper(decorated, f)


class requires(object):
    """Decorator that executes a function if requirements are met"""
    def __init__(self, IsValid=True, Option=None, Values=anything,
                 ValidData=None):
        """
        IsValid : execute the function if self.Failed is False
        Option : a required option
        Values : required values associated with an option
        ValidData : data level requirements, this must be a function with the
            following signature: f(*args, **kwargs) returning True. NOTE: if
            ValidData returns False on the first item evaluated, the decorated
            function will be removed from the remaining workflow.
        """
        # self here is the requires object
        self.IsValid = IsValid
        self.Option = Option
        self.ValidData = ValidData

        if Values is anything:
            self.Values = anything
        elif Values is not_none:
            self.Values = not_none
        elif not isinstance(Values, set):
            if isinstance(Values, str):
                self.Values = Values
            elif isinstance(Values, Iterable):
                self.Values = set(Values)
            else:
                self.Values = set([Values])
        else:
            self.Values = Values

    def doShortCircuit(self, wrapped):
        if self.IsValid and (wrapped.Failed and wrapped.ShortCircuit):
            return True
        else:
            return False

    def __call__(self, f):
        """Wrap a function

        f : the function to wrap
        """
        ### not sure how I feel about having multiple functions in here.
        ### also, the handling of Data is a bit dirty as it is now replicated
        ### over these functions. It is ideal to keep the functions slim, thus
{'fraction_non_alphanumeric': 0.09583428181197359, 'fraction_numerical': 0.005690871841566128, 'mean_word_length': 2.59975419909873, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6847749', 'n_tokens_mistral': 2560, 'n_tokens_neox': 2439, 'n_words': 883}