Esempio n. 1
0
 def fillme(field=field, possiblefillers=possiblefillers):
     # For preference, use a filler that will certainly return clean information (i.e. sort by the number of dirty inputs and prefer the first - i.e. the one with fewer dirty inputs)
     for fillerfunction, dirtyinputs, anyinputsdirty in sorted([(f, dirties(), len(dirties()) > 0) for f, dirties in possiblefillers], using(lambda x: x[2])):
         if field not in fact or isblankfield(fact[field]) or anyinputsdirty:
             # Don't know what the last value was or it may have changed: recompute.
             #
             # We also recompute if the incoming field is blank: this can happen if we have
             # deleted the contents of a field, and then come back to update the fact by tabbing
             # away from a filled field. Now we have enough information to fill it, and musn't
             # just reuse the old version, which will be blank.
             #
             # Note that if the field was *generated* as blank one then it will have a marker
             # in it, and so we won't pointlessly recompute its blankness.
             log.info("Attempting to fill %s field -- dirty inputs are %s", field, dirtyinputs)
             result = fillerfunction()
             if result is None:
                 log.info("Filling %s failed -- falling back on another method", field)
                 continue
             
             dirty[field] = cond(field in fact, lambda: result != unmarkgeneratedfield(fact[field]), lambda: anyinputsdirty)
             return result
         else:
             # Last value must not have changed: retain it
             assert (field in fact and not anyinputsdirty)
             log.info("Retaining old field value for %s (blank: %s)", field, isblankfield(fact[field]))
             dirty[field] = False
             return unmarkgeneratedfield(fact[field])
     
     # What if all of the possible updaters failed? Ideally we would not be in the graph at all, but it's too late for that.
     # All we can do is return None, and deal with this possibility later on.
     log.info("All possible updaters failed for the field %s", field)
     dirty[field] = False
     return None
Esempio n. 2
0
 def fillme(field=field, possiblefillers=possiblefillers):
     # For preference, use a filler that will certainly return clean information (i.e. sort by the number of dirty inputs and prefer the first - i.e. the one with fewer dirty inputs)
     for fillerfunction, dirtyinputs, anyinputsdirty in sorted([(f, dirties(), len(dirties()) > 0) for f, dirties in possiblefillers], using(lambda x: x[2])):
         if field not in fact or isblankfield(fact[field]) or anyinputsdirty:
             # Don't know what the last value was or it may have changed: recompute.
             #
             # We also recompute if the incoming field is blank: this can happen if we have
             # deleted the contents of a field, and then come back to update the fact by tabbing
             # away from a filled field. Now we have enough information to fill it, and musn't
             # just reuse the old version, which will be blank.
             #
             # Note that if the field was *generated* as blank one then it will have a marker
             # in it, and so we won't pointlessly recompute its blankness.
             log.info("Attempting to fill %s field -- dirty inputs are %s", field, dirtyinputs)
             result = fillerfunction()
             if result is None:
                 log.info("Filling %s failed -- falling back on another method", field)
                 continue
             
             dirty[field] = cond(field in fact, lambda: result != unmarkgeneratedfield(fact[field]), lambda: anyinputsdirty)
             return result
         else:
             # Last value must not have changed: retain it
             assert (field in fact and not anyinputsdirty)
             log.info("Retaining old field value for %s (blank: %s)", field, isblankfield(fact[field]))
             dirty[field] = False
             return unmarkgeneratedfield(fact[field])
     
     # What if all of the possible updaters failed? Ideally we would not be in the graph at all, but it's too late for that.
     # All we can do is return None, and deal with this possibility later on.
     log.info("All possible updaters failed for the field %s", field)
     dirty[field] = False
     return None
Esempio n. 3
0
def filledgraphforupdaters(all_updaters, fact, delta):
    graph = {}
    dirty = {}
    
    finddirties = lambda usings: [using for using in usings if dirty[using]]
    
    # The initial shell contains just the stuff that is non-generated or being set in this round.
    # Everything else will be generated based off of these values
    initiallyfilledfields = set([field for field in fact if not isgeneratedfield(field, fact[field]) and not isblankfield(fact[field])]).union(set(delta.keys()))
    for field in initiallyfilledfields:
        dirty[field] = field in delta
        graph[field] = (False, Thunk(lambda field=field: cond(field in delta, lambda: delta[field], lambda: fact[field])))
    
    log.info("Initially filled graph fields: %r", dirty)
    
    # Remove useless updaters, and updaters that might confound a delta by updating a field from an
    # old field. For example, if we change the reading we want to regenerate the color field -- but this is no
    # good if we just use the updater that gets the reading from the expression!
    all_updaters = [updater for updater in all_updaters if updater[0] not in initiallyfilledfields]
    delta_updaters = maydependon(all_updaters, delta.keys())
    
    # A complication is the presence of non-generated blank fields. We want to try and update these even if the
    # delta is empty (for example). To this end we need to make sure that the cut doesn't exclude updaters that
    # might potentially be depended on by an updater for a blank field.
    #
    # We also exclude updaters for any field that is going to get filled out by any delta_updater, because those
    # updaters should take priority.
    all_updaters = [updater for updater in all_updaters if all([updater[0] not in delta_field for delta_field, _, _ in delta_updaters])]
    blank_updaters = dependedonby(all_updaters, [field for field in fact if isblankfield(fact[field])])
    
    # Yes, this really works! This is because Python has reference comparison semantics on functions.
    # However, this is the reason we need to use tuples of fields we depend on, rather than lists
    updaters = set(delta_updaters + blank_updaters)
    
    def shell(alreadyfilled):
        # Gather all the fields we are newly able to fill given the most recent changes
        # to the list of already filled things
        cannowfill = FactoryDict(lambda _: [])
        for updatewhat, updatefunction, updateusings in updaters:
            if updatewhat not in alreadyfilled and all([updateusing in alreadyfilled for updateusing in updateusings]):
                # We have to delay the computation of whether something is dirty or not until we are inside
                # the actual thunk for this particular field, hence all the thunking and lambdas here
                inputs = Thunk(lambda updateusings=updateusings: [graph[updateusing][1]() for updateusing in updateusings])
                cannowfill[updatewhat].append((lambda inputs=inputs, updatefunction=updatefunction: updatefunction(*(inputs())),
                                               lambda inputs=inputs, updateusings=updateusings: seq(inputs(), lambda: finddirties(updateusings))))
        
        # Check for quiescence
        if len(cannowfill) == 0:
            # NB: could do something with the unfilled set (self.updateablefields.difference(alreadyfilled)) here
            return
        
        # Set up each fillable graph field with a thunk computing the value
        for field, possiblefillers in cannowfill.items():
            def fillme(field=field, possiblefillers=possiblefillers):
                # For preference, use a filler that will certainly return clean information (i.e. sort by the number of dirty inputs and prefer the first - i.e. the one with fewer dirty inputs)
                for fillerfunction, dirtyinputs, anyinputsdirty in sorted([(f, dirties(), len(dirties()) > 0) for f, dirties in possiblefillers], using(lambda x: x[2])):
                    if field not in fact or isblankfield(fact[field]) or anyinputsdirty:
                        # Don't know what the last value was or it may have changed: recompute.
                        #
                        # We also recompute if the incoming field is blank: this can happen if we have
                        # deleted the contents of a field, and then come back to update the fact by tabbing
                        # away from a filled field. Now we have enough information to fill it, and musn't
                        # just reuse the old version, which will be blank.
                        #
                        # Note that if the field was *generated* as blank one then it will have a marker
                        # in it, and so we won't pointlessly recompute its blankness.
                        log.info("Attempting to fill %s field -- dirty inputs are %s", field, dirtyinputs)
                        result = fillerfunction()
                        if result is None:
                            log.info("Filling %s failed -- falling back on another method", field)
                            continue
                        
                        dirty[field] = cond(field in fact, lambda: result != unmarkgeneratedfield(fact[field]), lambda: anyinputsdirty)
                        return result
                    else:
                        # Last value must not have changed: retain it
                        assert (field in fact and not anyinputsdirty)
                        log.info("Retaining old field value for %s (blank: %s)", field, isblankfield(fact[field]))
                        dirty[field] = False
                        return unmarkgeneratedfield(fact[field])
                
                # What if all of the possible updaters failed? Ideally we would not be in the graph at all, but it's too late for that.
                # All we can do is return None, and deal with this possibility later on.
                log.info("All possible updaters failed for the field %s", field)
                dirty[field] = False
                return None
            
            graph[field] = (True, Thunk(fillme))
        
        shell(alreadyfilled.union(set(cannowfill.keys())))
    
    shell(initiallyfilledfields)
    
    return graph
Esempio n. 4
0
def filledgraphforupdaters(all_updaters, fact, delta):
    graph = {}
    dirty = {}
    
    finddirties = lambda usings: [using for using in usings if dirty[using]]
    
    # The initial shell contains just the stuff that is non-generated or being set in this round.
    # Everything else will be generated based off of these values
    initiallyfilledfields = set([field for field in fact if not isgeneratedfield(field, fact[field]) and not isblankfield(fact[field])]).union(set(delta.keys()))
    for field in initiallyfilledfields:
        dirty[field] = field in delta
        graph[field] = (False, Thunk(lambda field=field: cond(field in delta, lambda: delta[field], lambda: fact[field])))
    
    log.info("Initially filled graph fields: %r", dirty)
    
    # Remove useless updaters, and updaters that might confound a delta by updating a field from an
    # old field. For example, if we change the reading we want to regenerate the color field -- but this is no
    # good if we just use the updater that gets the reading from the expression!
    all_updaters = [updater for updater in all_updaters if updater[0] not in initiallyfilledfields]
    delta_updaters = maydependon(all_updaters, delta.keys())
    
    # A complication is the presence of non-generated blank fields. We want to try and update these even if the
    # delta is empty (for example). To this end we need to make sure that the cut doesn't exclude updaters that
    # might potentially be depended on by an updater for a blank field.
    #
    # We also exclude updaters for any field that is going to get filled out by any delta_updater, because those
    # updaters should take priority.
    all_updaters = [updater for updater in all_updaters if all([updater[0] not in delta_field for delta_field, _, _ in delta_updaters])]
    blank_updaters = dependedonby(all_updaters, [field for field in fact if isblankfield(fact[field])])
    
    # Yes, this really works! This is because Python has reference comparison semantics on functions.
    # However, this is the reason we need to use tuples of fields we depend on, rather than lists
    updaters = set(delta_updaters + blank_updaters)
    
    def shell(alreadyfilled):
        # Gather all the fields we are newly able to fill given the most recent changes
        # to the list of already filled things
        cannowfill = FactoryDict(lambda _: [])
        for updatewhat, updatefunction, updateusings in updaters:
            if updatewhat not in alreadyfilled and all([updateusing in alreadyfilled for updateusing in updateusings]):
                # We have to delay the computation of whether something is dirty or not until we are inside
                # the actual thunk for this particular field, hence all the thunking and lambdas here
                inputs = Thunk(lambda updateusings=updateusings: [graph[updateusing][1]() for updateusing in updateusings])
                cannowfill[updatewhat].append((lambda inputs=inputs, updatefunction=updatefunction: updatefunction(*(inputs())),
                                               lambda inputs=inputs, updateusings=updateusings: seq(inputs(), lambda: finddirties(updateusings))))
        
        # Check for quiescence
        if len(cannowfill) == 0:
            # NB: could do something with the unfilled set (self.updateablefields.difference(alreadyfilled)) here
            return
        
        # Set up each fillable graph field with a thunk computing the value
        for field, possiblefillers in cannowfill.items():
            def fillme(field=field, possiblefillers=possiblefillers):
                # For preference, use a filler that will certainly return clean information (i.e. sort by the number of dirty inputs and prefer the first - i.e. the one with fewer dirty inputs)
                for fillerfunction, dirtyinputs, anyinputsdirty in sorted([(f, dirties(), len(dirties()) > 0) for f, dirties in possiblefillers], using(lambda x: x[2])):
                    if field not in fact or isblankfield(fact[field]) or anyinputsdirty:
                        # Don't know what the last value was or it may have changed: recompute.
                        #
                        # We also recompute if the incoming field is blank: this can happen if we have
                        # deleted the contents of a field, and then come back to update the fact by tabbing
                        # away from a filled field. Now we have enough information to fill it, and musn't
                        # just reuse the old version, which will be blank.
                        #
                        # Note that if the field was *generated* as blank one then it will have a marker
                        # in it, and so we won't pointlessly recompute its blankness.
                        log.info("Attempting to fill %s field -- dirty inputs are %s", field, dirtyinputs)
                        result = fillerfunction()
                        if result is None:
                            log.info("Filling %s failed -- falling back on another method", field)
                            continue
                        
                        dirty[field] = cond(field in fact, lambda: result != unmarkgeneratedfield(fact[field]), lambda: anyinputsdirty)
                        return result
                    else:
                        # Last value must not have changed: retain it
                        assert (field in fact and not anyinputsdirty)
                        log.info("Retaining old field value for %s (blank: %s)", field, isblankfield(fact[field]))
                        dirty[field] = False
                        return unmarkgeneratedfield(fact[field])
                
                # What if all of the possible updaters failed? Ideally we would not be in the graph at all, but it's too late for that.
                # All we can do is return None, and deal with this possibility later on.
                log.info("All possible updaters failed for the field %s", field)
                dirty[field] = False
                return None
            
            graph[field] = (True, Thunk(fillme))
        
        shell(alreadyfilled.union(set(cannowfill.keys())))
    
    shell(initiallyfilledfields)
    
    return graph