def set_default_attrs(
    obj, **kws
):  # e if this was general, we could refile into py_utils, but it turns out it's not general enough
    """For each attr=val pair in **kws, if attr is not set in obj, set it, without doing usage or change tracking.
    If obj supports __setattr_default__ (not a special name to Python itself), use that, otherwise use hasattr and setattr
    (with a temporary dynamic suppression of usage or change tracking, if possible -- nim for now except as an assertion).
       Note: this is not very efficient (or self-contained) for general use, for which pure hasattr/setattr would be sufficient.
    """
    # Note about why we disable tracking: hasattr itself does legitimately track use of the attr,
    # since testing thereness does use its "value" in a general sense (as if value was _UNSET_ when not there).
    # But, in this specific code, we don't want client (caller) to see us using attr,
    # since from client POV, all we're doing is changing _UNSET_ to v, which are equivalent values
    # (as client is declaring by calling us).
    # So we need to discard tracked usage of attr here.
    # We also need to discard any change from the set to default value,
    # except that we can probably ignore that issue for now (due to an accident of how current code uses this),
    # since if the attr is unset, it was never yet used, so invalidating it should have no effect.
    if 1:
        from exprs.Exprs import is_pure_expr

        if is_pure_expr(obj):
            assert 0, (
                "set_default_attrs called on pure_expr %r is almost surely a bug; normally do it in _init_instance"
                % (obj,)
            )
    import foundation.changes as changes

    try:
        method = obj.__setattr_default__
    except AttributeError:
        ## print "fyi: set_default_attrs using general case for",obj,kws.keys() # this happens for all non-tracked StatePlaces
        # general case - let's hope the object doesn't have tracked attrs; the temporary assertion here might not catch this
        ###e instead, we should actively suppress usage/change tracking, so we can permit it in obj
        # (tho careless uses of that would lead to bugs)
        mc = changes.begin_disallowing_usage_tracking("set_default_attrs (hasattr/setattr) for %r" % obj)
        # note: the argument is just an explanation for use in error messages ##e OPTIM: don't precompute that arg
        try:
            for k, v in kws.iteritems():
                if not hasattr(obj, k):
                    setattr(obj, k, v)
        finally:
            changes.end_disallowing_usage_tracking(mc)
    else:
        # use the special method
        mc = changes.begin_disallowing_usage_tracking("set_default_attrs (__setattr_default__) for %r" % obj)
        # e optim: remove this check; it only catches usage tracking, not change tracking (ie inval of something by this), anyway
        try:
            for k, v in kws.iteritems():
                method(
                    k, v
                )  # does its own usage/change-tracking suppression (we assume); this is partially checked (for now)
        finally:
            changes.end_disallowing_usage_tracking(mc)
    return  # from set_default_attrs
Exemple #2
0
def set_default_attrs(
    obj, **kws
):  #e if this was general, we could refile into py_utils, but it turns out it's not general enough
    """For each attr=val pair in **kws, if attr is not set in obj, set it, without doing usage or change tracking.
    If obj supports __setattr_default__ (not a special name to Python itself), use that, otherwise use hasattr and setattr
    (with a temporary dynamic suppression of usage or change tracking, if possible -- nim for now except as an assertion).
       Note: this is not very efficient (or self-contained) for general use, for which pure hasattr/setattr would be sufficient.
    """
    # Note about why we disable tracking: hasattr itself does legitimately track use of the attr,
    # since testing thereness does use its "value" in a general sense (as if value was _UNSET_ when not there).
    # But, in this specific code, we don't want client (caller) to see us using attr,
    # since from client POV, all we're doing is changing _UNSET_ to v, which are equivalent values
    # (as client is declaring by calling us).
    # So we need to discard tracked usage of attr here.
    # We also need to discard any change from the set to default value,
    # except that we can probably ignore that issue for now (due to an accident of how current code uses this),
    # since if the attr is unset, it was never yet used, so invalidating it should have no effect.
    if 1:
        from exprs.Exprs import is_pure_expr
        if is_pure_expr(obj):
            assert 0, "set_default_attrs called on pure_expr %r is almost surely a bug; normally do it in _init_instance" % (
                obj, )
    import foundation.changes as changes
    try:
        method = obj.__setattr_default__
    except AttributeError:
        ## print "fyi: set_default_attrs using general case for",obj,kws.keys() # this happens for all non-tracked StatePlaces
        # general case - let's hope the object doesn't have tracked attrs; the temporary assertion here might not catch this
        ###e instead, we should actively suppress usage/change tracking, so we can permit it in obj
        # (tho careless uses of that would lead to bugs)
        mc = changes.begin_disallowing_usage_tracking(
            'set_default_attrs (hasattr/setattr) for %r' % obj)
        # note: the argument is just an explanation for use in error messages ##e OPTIM: don't precompute that arg
        try:
            for k, v in kws.iteritems():
                if not hasattr(obj, k):
                    setattr(obj, k, v)
        finally:
            changes.end_disallowing_usage_tracking(mc)
    else:
        # use the special method
        mc = changes.begin_disallowing_usage_tracking(
            'set_default_attrs (__setattr_default__) for %r' % obj)
        #e optim: remove this check; it only catches usage tracking, not change tracking (ie inval of something by this), anyway
        try:
            for k, v in kws.iteritems():
                method(
                    k, v
                )  # does its own usage/change-tracking suppression (we assume); this is partially checked (for now)
        finally:
            changes.end_disallowing_usage_tracking(mc)
    return  # from set_default_attrs
Exemple #3
0
 def __getitem__(self, key):
     try:
         return dict.__getitem__(self, key)
     except KeyError:
         import foundation.changes as changes
         mc = changes.begin_disallowing_usage_tracking(self) # note: argument is just explanation for use in error messages
         try:
             val = self._way(key)
                 # note, it needs to be legal for something during this to reallow using tracking for itself, locally
         except:
             # try not to hurt this object for use at other keys, but good to reraise exception to whoever accessed this key
             # (maybe something's wrong with this key, but good even if not, i think [061121])
             # Note: the exception might be an error being reported by begin_disallowing_usage_tracking!
             # So we should do the ending of that disallowing -- the initial raising of that exception should not do it.
             changes.end_disallowing_usage_tracking(mc)
             raise
         else:
             changes.end_disallowing_usage_tracking(mc)
             dict.__setitem__(self, key, val)
             return val
     pass
Exemple #4
0
 def __getitem__(self, key):
     try:
         return dict.__getitem__(self, key)
     except KeyError:
         import foundation.changes as changes
         mc = changes.begin_disallowing_usage_tracking(self) # note: argument is just explanation for use in error messages
         try:
             val = self._way(key)
                 # note, it needs to be legal for something during this to reallow using tracking for itself, locally
         except:
             # try not to hurt this object for use at other keys, but good to reraise exception to whoever accessed this key
             # (maybe something's wrong with this key, but good even if not, i think [061121])
             # Note: the exception might be an error being reported by begin_disallowing_usage_tracking!
             # So we should do the ending of that disallowing -- the initial raising of that exception should not do it.
             changes.end_disallowing_usage_tracking(mc)
             raise
         else:
             changes.end_disallowing_usage_tracking(mc)
             dict.__setitem__(self, key, val)
             return val
     pass
Exemple #5
0
    def _set_defaultValue(self, defaultValue):
        """If attr is not set, set it to defaultValue; never do any usage or change tracking.
        (WARNING: This will cause bugs unless all providers of access to a given attribute-instance
         use this consistently (same default value, and all of them using this rather than some of
         them providing access to the attr with no default value); they must all use it before
         providing access to that attr to any client object.)
        """
        # see comments in set_default_attrs about why we need this to do no usage or change tracking
        if self.valid:
            return # easy case

        # Dilemma: we might have no value, or we might have one computable by an initial-value compute method...
        # or we might have such a method which will raise LvalError_ValueIsUnset when we try it
        # (e.g. valfunc method in some client code). The only way to find out is to use that method,
        # but what if doing so tracked some usage? I think it's normal for that to happen...
        # OTOH, it's not correct, I think, to have two inconsistent ways of setting a default value --
        # code that calls this, vs. code that supplies an initial-value compute method that actually works.
        # So, maybe we should be able to ask the method which kind it is... but for now, assume it could
        # be either. If it computes a real value, we better not run it now, since usage would get tracked...
        # though that's an error... one way to detect is to return now and let it run, but that led to infrecur
        # in self.delegate in If.cond (don't know why). But setting defaultValue now risks hiding bugs.
        # That leaves: run it now, asserting no usage tracking. [Note: current calling code redundantly asserts the same thing. #e]
        #
        # One case which is not covered: something asked if we were set, was told "we're unset", and tracked its usage of us.
        # Ideally we'd assert right here that we have no current inval-subscribers. ####e DOIT
        # (For now, if we *do* have subscribers who learned we were unset, and if we leave them untouched,
        # it's indeed a bug that they asked that before we were initialized, but it's not so bad that they'll
        # get invalled when something changes us later.)
        printnim("assert no current inval-subscribers")
        
        mc = changes.begin_disallowing_usage_tracking('_set_defaultValue in %r' % self)
            # note: the argument is just an explanation for use in error messages ##e OPTIM: don't precompute that arg
        try:
            try:
                val = self._compute_value() # don't call get_value since it calls track_use
            except LvalError_ValueIsUnset:
                # this is the only non-error case!
                # (the following just inlines self.set_constant_value(defaultValue):)
                self._value = defaultValue
                self.valid = True
                ## print "_set_defaultValue returning after set"
                return
            except:
                # any other exception, including discovering usage tracking in that computation [once that works when it happens],
                # should be reported, but then (I think) needn't prevent this from working.
                print_compact_traceback("error: exception (ignored) in _set_defaultValue trying initval-computation in %r: " % self)
                self._value = defaultValue
                self.valid = True
                return
            else:
                # If a value was computed, that conflicts with using this method... report that error (even if the values agree),
                # but which value should we use? Let's use the computed one for now.
                #   NOTE: In present code [061121], this can also happen if usage tracking occurred, since it's not detected
                # until the end_disallowing_usage_tracking call below (since begin_disallowing_usage_tracking is not fully
                # implemented).
                print "error: %r computed initial value %r (using it) but was also given an explicit _set_defaultValue(%r)" % \
                      (self, val, defaultValue)
                self._value = val
                self.valid = True
                return
            pass
        finally:
            changes.end_disallowing_usage_tracking(mc)
        pass # end of method _set_defaultValue
Exemple #6
0
    def _set_defaultValue(self, defaultValue):
        """If attr is not set, set it to defaultValue; never do any usage or change tracking.
        (WARNING: This will cause bugs unless all providers of access to a given attribute-instance
         use this consistently (same default value, and all of them using this rather than some of
         them providing access to the attr with no default value); they must all use it before
         providing access to that attr to any client object.)
        """
        # see comments in set_default_attrs about why we need this to do no usage or change tracking
        if self.valid:
            return  # easy case

        # Dilemma: we might have no value, or we might have one computable by an initial-value compute method...
        # or we might have such a method which will raise LvalError_ValueIsUnset when we try it
        # (e.g. valfunc method in some client code). The only way to find out is to use that method,
        # but what if doing so tracked some usage? I think it's normal for that to happen...
        # OTOH, it's not correct, I think, to have two inconsistent ways of setting a default value --
        # code that calls this, vs. code that supplies an initial-value compute method that actually works.
        # So, maybe we should be able to ask the method which kind it is... but for now, assume it could
        # be either. If it computes a real value, we better not run it now, since usage would get tracked...
        # though that's an error... one way to detect is to return now and let it run, but that led to infrecur
        # in self.delegate in If.cond (don't know why). But setting defaultValue now risks hiding bugs.
        # That leaves: run it now, asserting no usage tracking. [Note: current calling code redundantly asserts the same thing. #e]
        #
        # One case which is not covered: something asked if we were set, was told "we're unset", and tracked its usage of us.
        # Ideally we'd assert right here that we have no current inval-subscribers. ####e DOIT
        # (For now, if we *do* have subscribers who learned we were unset, and if we leave them untouched,
        # it's indeed a bug that they asked that before we were initialized, but it's not so bad that they'll
        # get invalled when something changes us later.)
        printnim("assert no current inval-subscribers")

        mc = changes.begin_disallowing_usage_tracking(
            '_set_defaultValue in %r' % self)
        # note: the argument is just an explanation for use in error messages ##e OPTIM: don't precompute that arg
        try:
            try:
                val = self._compute_value(
                )  # don't call get_value since it calls track_use
            except LvalError_ValueIsUnset:
                # this is the only non-error case!
                # (the following just inlines self.set_constant_value(defaultValue):)
                self._value = defaultValue
                self.valid = True
                ## print "_set_defaultValue returning after set"
                return
            except:
                # any other exception, including discovering usage tracking in that computation [once that works when it happens],
                # should be reported, but then (I think) needn't prevent this from working.
                print_compact_traceback(
                    "error: exception (ignored) in _set_defaultValue trying initval-computation in %r: "
                    % self)
                self._value = defaultValue
                self.valid = True
                return
            else:
                # If a value was computed, that conflicts with using this method... report that error (even if the values agree),
                # but which value should we use? Let's use the computed one for now.
                #   NOTE: In present code [061121], this can also happen if usage tracking occurred, since it's not detected
                # until the end_disallowing_usage_tracking call below (since begin_disallowing_usage_tracking is not fully
                # implemented).
                print "error: %r computed initial value %r (using it) but was also given an explicit _set_defaultValue(%r)" % \
                      (self, val, defaultValue)
                self._value = val
                self.valid = True
                return
            pass
        finally:
            changes.end_disallowing_usage_tracking(mc)
        pass  # end of method _set_defaultValue