# Copyright 2006-2007 Nanorex, Inc. See LICENSE file for details. """ staterefs.py - facilities for defining and referencing state, in widget exprs which display it, edit it, operate on it, produce it ###e MIGHT RENAME, since widget_env has an attr or arg named this. state.py? might be confusing re State macro. @author: bruce @version: $Id$ @copyright: 2006-2007 Nanorex, Inc. See LICENSE file for details. note: this used to define StatePlace and associated things, but those are now in StatePlace.py [as of 061203]; most things remaining here are nim see also: class Set, and State macro, in other files """ from exprs.StatePlace import set_default_attrs from exprs.instance_helpers import InstanceOrExpr from exprs.attr_decl_macros import Arg, ArgOrOption from exprs.ExprsConstants import Type from exprs.py_utils import printnim, stub from exprs.__Symbols__ import Anything from utilities import debug_flags # == class LocalVariable_StateRef(InstanceOrExpr): # guess, 061130 # [moved here from controls.py, 061203; will probably become obs once State works] "return something which instantiates to something with .value which is settable state..." #e older name: StateRefFromIpath; is this almost the same as the proposed State() thing? it may differ in how to alter ipath # or some other arg saying where to store the ref, or in not letting you change how to store it (value encoding), # and worst, in whether it's an lval or not -- I think State is an lval (no need for .value) and this is a stateref. type = Arg(Type, Anything) defaultValue = ArgOrOption(Anything, None) ##e default of this should depend on type, in same way it does for Arg or Option # see comments in about problems if this is a formula which uses anything usage-tracked -- same probably applies here. def get_value(self): #e should coerce this to self.type before returning it -- or add glue code wrt actual type, or.... return self.transient_state.value ###e let transient_state not be the only option? does the stateplace even matter?? def set_value(self, val): self.transient_state.value = val #e should coerce that to self.type, or add glue code... return value = property(get_value, set_value) def _init_instance(self): super(LocalVariable_StateRef, self)._init_instance() set_default_attrs( self.transient_state, value = self.defaultValue) #e should coerce that to self.type pass class PrefsKey_StateRef(InstanceOrExpr): # guess, 061204 """return something which instantiates to something with .value which is settable state, shared with env.prefs[key], properly usage-tracked in a compatible way with the rest of NE1 """ prefs_key = Arg(str) # note: it's probably ok if this is a time-varying formula, tho I don't know why it would be useful. defaultValue = ArgOrOption(Anything, None) ##e default of this should depend on type, in same way it does for Arg or Option -- # nonetheless it's so much more common to specify a default value than a type, that I decided to put default value first. # ##BUG (optimization issue only, and not yet important in practice): # [re-noticed 070228 -- also the topic of the printnim and older comment just below!] # this defaultValue should be evaluated with usage tracking discarded, unless prefs_key changes, # in which case it should be reevalled once (doable if its eval pretends to use prefs_key) -- except ideally the value # for older prefs_keys being reused again would be cached (eg using MemoDict) (not important in practice). # This bug is not yet serious since the actual args so far are constants [as of 070228]. # It's purely an optimization issue; but the worst-case effects are that the default value changes a lot more # often than the prefs value itself, causing needless inval propogation into the get_value caller, # and thus needless recomputation of an arbitrary amount of memoized results which care about the prefs value. # (But for all I know, the usual constant case would slow down due to the overhead of discarding usage tracking. # Actually that's not an issue since we'd also rarely recompute it, not nearly on every get_value.) printnim("need a way to declare that this arg should not be usage-tracked, or have its use as default val do that for that use") # [older version of the comment above; contains still-useful suggestions:] ###e we need a way to declare that this arg should not be usage-tracked re time-variation!!! or to assert it uses nothing. # right now, if it uses something that will silently cause bugs, probably just invisible performance bugs from extra invals. # CAN THERE BE A GENERAL SOLN based on what we use this for (default vals of things that don't care about updates)? # that is more correct in principle, since that's what matters -- eg what if someone added another use of the same arg. ###e type = ArgOrOption(Type, Anything) # this arg is not yet passed by anything, or used in this implem; # moved type from arg2->arg3, and Arg -> ArgOrOption (as being after ArgOrOption arg2 ought to force anyway), 061211 night def get_value(self): #e should coerce this to self.type before returning it -- or add glue code wrt actual type, or.... prefs_key = self.prefs_key assert type(prefs_key) == type("") #k redundant? import foundation.env as env return env.prefs.get( prefs_key, self.defaultValue ) # note: this computes defaultValue at least once, even if not needed. def set_value(self, val): #e should coerce that to self.type, or add glue code... prefs_key = self.prefs_key assert type(prefs_key) == type("") #k redundant? import foundation.env as env env.prefs[prefs_key] = val if 0 and debug_flags.atom_debug: print "fyi: %r set env.prefs[%r] = %r" % (self, prefs_key, val) return value = property(get_value, set_value) def _init_instance(self): super(PrefsKey_StateRef, self)._init_instance() # also set default value of this prefs key (noting that it may or may not already have a saved value) -- # i think this (since it does .get with default arg) is the official way: self.get_value() pass # == end of current code class InstanceOrExpr_Stub: pass ### kluge, permit early import of this file [061126 late] -- ##e probably need to move LocalState into another file class LocalState(InstanceOrExpr_Stub): #e stub, just reserve the name and let searches find where to add code for it """Permit body exprs to reference specific external state using local names (and perhaps using specified proxy-wrappers, so the state is seen using locally desired types & in local coordinate systems). Usage: - if x is already defined as a Symbol, e.g. by from __Symbols__ import x : maybe: LocalState( (x,type/location/etc), body(x) ) - otherwise: maybe: LocalState( lambda x = XXX(type/location/etc): body(x) ) # might work if we never modify x directly, only use it or set attrs in it """ _init_instance = stub # asfail if used pass ''' # e.g. code, scratch area [bruce 070817 made this a string, since as 'if 0' it was causing # a traceback in pychecker, according to Eric M mail to cad list.] LocalState( lambda x = State(int, 1): body(x.value, x.value = 1) ) # note, x.value = 1 is not allowed in a lambda anyway! # in a class: def body(self, x): x.value x.value = 1 ''' #e see also ToggleShow.py # end