summaryrefslogtreecommitdiff
path: root/cad/src/exprs/instance_helpers.py
blob: 41a17f303031e3b6317111cbdc061485af46fe08 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
# Copyright 2006-2008 Nanorex, Inc.  See LICENSE file for details. 
"""
instance_helpers.py -- provides InstanceOrExpr and some related things

@author: Bruce
@version: $Id$
@copyright: 2006-2008 Nanorex, Inc.  See LICENSE file for details. 

History (partial):

070129 moved still-nim GlueCodeMemoizer into new file Column_old_nim.py

070815 split IorE_guest_mixin superclass out of InstanceOrExpr
and moved it into a new file
"""

from exprs.lvals import LvalError_ValueIsUnset
## from lvals import LvalDict1 # only needed by obs code

from exprs.widget_env import thisname_of_class, widget_env #e refile import?? or make it an env method??

from utilities.debug import print_compact_traceback
from utilities.debug import print_compact_stack

from exprs.Exprs import is_pure_expr
from exprs.Exprs import is_expr_Instance
from exprs.Exprs import SymbolicExpr, canon_expr
from exprs.Exprs import expr_is_Instance
from exprs.Exprs import expr_constant_value
from exprs.Exprs import is_Expr_pyclass
from exprs.Exprs import EVAL_REFORM

from exprs.py_utils import printnim
from exprs.py_utils import printfyi

from exprs.IorE_guest_mixin import IorE_guest_mixin
from exprs.StatePlace import StatePlace
from exprs.attr_decl_macros import Instance, Arg
from exprs.ExprsConstants import normalize_color
from exprs.__Symbols__ import _self

# ==

# maybe merge this into InstanceOrExpr docstring:
"""
Instances of subclasses of this can be unplaced or placed (aka "instantiated");
if placed, they might be formulas (dependent on aspects of drawing-env state)
for appearance and behavior (justifying the name Drawable),
or for some value used in that (e.g. a color, vector, string).
   Specific subclasses, e.g. Rect, contain code which will be used in placed instances (mainly drawing code)
unless the env provided an overriding expansion for exprs headed by that subclass.
If it did, the instance created by placing such an expr will (usually) have some other class.
"""

# note about nonexistent class IorE: IorE is a common abbreviation for class InstanceOrExpr.

class InstanceOrExpr(IorE_guest_mixin): # see docstring for discussion of the basic kluge of using one class for both
    """
    Main superclass for specific kinds of Instance classes whose python instances can be either Instances or Exprs,
    and (more importantly for the user) whose use as a constructor usually constructs an Expr.
    Used (for example) for Column, Rect, If, Widget2D, etc. See elsewhere for general explanation [#nim].
       The rest of this docstring discusses some fine points of the class semantics and implementation,
    and possible changes to them.
       Kluge: the same class is used to represent both Exprs and Instances of the same type; the difference is encoded
    in self._e_is_instance, and is constant throughout one python-instance's lifetime (after it's fully constructed,
    which means, after __init__ and perhaps after immediately subsequent private methods in another Expr which created it).
    Many methods either assert a specific state for self._e_is_instance, or branch on it (or ought to #e).
       Pro: developer can create and use just one class, e.g. Rect, needing no other setup/registration except defining it,
    to both hold the Rect Instance methods, and serve as the Rect Expr constructor. The Instance methods can be written
    (after an initial assertion) as if self is the Instance (as opposed to self.instance, or some argument they receive),
    and (to avoid unpleasant surprises) that's actually true. And the class as constructor always constructs a Python instance
    of itself, as one would expect.
       Con: the Instance methods could be accidently requested in an Expr version, or vice versa; their very presence
    might mess up some semantics, since it's generally bad if expr.attr does anything except construct a getattr_Expr.
    The motivations above are not obviously good enough to justify the bug risk and internal-method unclarity.
       Possible issues in present implem: constant_Expr(10) is in some sense both an Expr *and* an Instance. The predicates
    don't yet account for this. And logic bugs in the whole scheme may yet show up.
       Possible alternative: these classes could represent only Instances, but still be usable as Expr constructors,
    by having __new__ create a separate (nim) ExprForInstanceOrExpr object which is not a subclass of this class,
    but which knows which subclass of this class to create when it needs to make Instances from itself. If this was done,
    much of the code in this specific class InstanceOrExpr would be moved to ExprForInstanceOrExpr, which might be renamed
    to be more general since it would really apply to any Expr which can be called, remaining an Expr but having its
    option formulas customized or its arg formulas supplied, and can then later be instantiated in an env to produce
    a specific Instance based on those formulas.
    """

    #e rename to: ExprOrInstance?
    #  [maybe, until i rename the term Instance, if i do... con side: most methods in each one are mainly for Instance, except arg decls,
    #   and even they can be thought of mostly as per-instance-evalled formulas. So when coding it, think Instance first.]
    # PatternOrThing?[no]
    ### WARNING: instantiate normally lets parent env determine kid env... if arg is inst this seems reversed...
    # may only be ok if parent doesn't want to modify lexenv for kid.

    # standard stateplaces (other ones can be set up in more specific subclasses)
    # [following 3 StatePlaces moved here from Highlightable & ToggleShow, 061126 late;
    #  these comments not yet reviewed for their new context: ###doc]
    #
    # refs to places to store state of different kinds, all of which is specific to this Instance (or more precisely to its ipath)
    ##e [these will probably turn out to be InstanceOrExpr default formulae] [note: their 2nd arg ipath defaults to _self.ipath]
    # OPTIM QUESTION: which of these actually need usage/mod tracking? They all have it at the moment [not anymore, 061121],
    # but my guess is, per_frame_state doesn't need it, and not providing it would be a big optim. [turned out to be a big bugfix too!]
    # (We might even decide we can store all per_frame_state in self, not needing a StatePlace at all -- bigger optim, I think.
    #  That can be done once reloading or reinstancemaking is not possible during one frame. I'm not sure if it can happen then,
    #  even now... so to be safe I want to store that state externally.)

    # transient_state moved to a superclass 070815
    glpane_state = StatePlace('glpane', tracked = False) # state which is specific to a given glpane [#e will some need tracked=True?]
    per_frame_state = StatePlace('per_frame', tracked = False)
        # state which is only needed while drawing one frame (someday, cleared often)
        # (this is probably also specific to our glpane; note that a given Instance has only one glpane)
    model_state = StatePlace('model') # state that's part of the model (i.e. stored/copied with model objects) ### NOT YET USED [070213]
    untracked_model_state = StatePlace('untracked_model', tracked = False)
        # model state, not change/usage-tracked (e.g. counters for allocating unique index-components or name-components) [070213]
    # abbrevs for read-only state [#e should we make them a property or so, so we can set them too?]
    glname = glpane_state.glname # (note, most things don't have this)

    # instance variable defaults (names must start with _e_ or _i_, or they'll cause trouble for non-Instance getattr_Expr formation)
    _i_model_type = None # experimental, perhaps a kluge, 070206; WARNING: set externally on some specific Instances in world.py
    ## _e_model_type = None # 070215 (not sure if it supercedes the above) -- type of model obj an expr intends to construct (if known)
        # note: the value of _e_model_type can just be a string, or a fancier object (###NIM and probably not yet handled)
        # do we compute a type on exprs which can be _e_model_type or classname but only for ModelObjects? (else delegate to one?)
        # (or for pure graphicals can it be classname too? yes, they can be shapes in the model -- maybe their class needs to say so?)
        # do instances ever have a different model_type than their expr predicted?
        # (seems possible -- main use of expr one is for sbar_texts & cmenu item texts, ie UI predictions of what an expr can make)
        # sceptic: do exprs need this, or do we need special expr-like Instances to reside in eg PalletteWell, which have a special
        # interface "maker" and know the type of what they can make? But aren't exprs similar to makers anyway?
        # (even if so, must they be wrapped to be one? evidence for that might be if there are any params about how to make with them.
        #  but i think not, since they're useable to make things "out of the box"... what if each thing wants params, do you want
        #  to customize that beyond what the expr knows about args/state, and think of that as maker params??)
        # Is the real issue for determining the attrname/methodname not "model" or "expr" but "what you make vs what you are"?? ###k

    def _e_model_type_you_make(self): ###k 070215 very experimental #e move below init
        """
        #doc; overridden on some subclasses, notably DelegatingInstanceOrExpr
        """
        # but lots of specific DIorE don't delegate it...
        # model obj DIorE don't, but graphical ones do. For now assume we know by whether it's subclass of ModelObj.
        # This implem is meant for graphical prims like Rect.
        # ok for expr but wrong for instance! actually, not true - inst makes itself! so wrong for an explicit Maker I guess...
        return self.__class__.__name__.split('.')[-1] ##e see where else we have that code, maybe world.py; have classattr to override?
    
    def __init__(self, *args, **kws):
        # note: just before any return herein, we must call self._e_init_e_serno(), so it's called after any canon_expr we do;
        # also, the caller (if a private method in us) can then do one inside us; see comment where this is handled in __call__
        ###e does place (instanceness) come in from kws or any args?
        # initialize attrs of self to an owned empty state for an expr
        self._e_kws = {} # need a fresh dict, since we own it and alter it during subsequent parts of init (??)
        # handle special keywords (assume they all want self to be initialized as we just did above)
        val = kws.pop('_copy_of', None)
        if val:
            assert not args and not kws
            self._destructive_copy(val)
            self._e_init_e_serno()
            return
        val = kws.pop('_make_in', None)
        if val:
            assert not args and not kws
            self._e_init_e_serno() #070121 moved this before _destructive_make_in, so it sees serno in prints,
                # and since AFAIK the desire to have later serno than our kids only applies to pure exprs, not Instances ##k
            self._destructive_make_in(val)
            return
        # assume no special keywords remain
        self._destructive_init(args, kws)
        IorE_guest_mixin.__init__(self) # must be done after _destructive_init
        return
    
    def __call__(self, *args, **kws):
        new = self._copy()
        new._destructive_init(args, kws)
        new._e_init_e_serno()
            # note, this "wastes a serno" by allocating a newer one that new.__init__ did;
            # this is necessary to make serno newer than any exprs produced by canon_expr during _destructive_init
        return new

# I'm not sure whether this unfinished _i_type and _e_delegate_type code will be used -- for now, leave it but comment it out [070206]
##    _e_delegate_type = False # subclass-specific constant -- whether Instance type determination should be delegated to self._delegate
##    
##    def _i_type(self): #070206
##        """Return something that represents the type of Instance or Expr we are.
##        (This is not the same as Python type. For more info on what we return, see ###doc.)
##           WARNING: preliminary implem, several implicit kluges.
##           NOTE: _e_delegate_type should be True in any delegating subclass, *if* the delegate's type
##        should be treated as self's type. This has to be determined by each specific subclass --
##        it's true for wrapper-like delegators (e.g. Translate)
##        but not true for Macro-like ones, and there is not yet a formal distinction between those
##        (though this makes it clear that there should be ###e).
##        """
##        if self._e_is_instance:
##            if self._e_delegate_type:
##                return self._delegate._i_type()
##            name = self.__class__.__name__
##        else:
##            name = "Expr" ###e include something about the predicted instance type? what if it's condition- or time-varying?
##                # maybe only include it if an outer level includes a type-coercer??
##        return name
    
    # deprecated public access to self._e_kws -- used by _DEFAULT_ decls
    def custom_compute_method(self, attr):###NAMECONFLICT?
        """
        return a compute method using our custom formula for attr, or None if we don't have one
        """
        try:
            formula = self._e_kws[attr]
        except KeyError:
            # caller will have to use the default case [#k i think]
            return None
        printnim("custom_compute_method likely BUG: uses wrong env for _self") # could fix by passing flag or env to _e_compute_method?
        printnim("assume it's a formula in _self; someday optim for when it's a constant, and/or support other symbols in it")
        #k guess: following printnim is wrong, should be in return None case -- verify then fix (or remove this entire feature)
        printfyi("something made use of deprecated _DEFAULT_ feature on attr %r" % (attr,)) ###e deprecated - remove uses, gradually
            # in fact, reviewing the uses 070120, there are none left, and the last ones in widget2d went away long ago. ###e ZAP IT
        return formula._e_compute_method(self, '@' + attr) #k index arg is a guess, 061110
    
    # copy methods (used by __call__)
    def _copy(self, _instance_warning = True):
        ## assert not self._e_is_instance ## ??? [061019]
        if self._e_is_instance and _instance_warning:
            print "WARNING: copying an instance %r" % (self,) # this might be ok...
        return self.__class__(_copy_of = self) # this calls _destructive_copy on the new instance
    def copy(self):#061213 experiment, used in demo_drag.py, background.copy(color=green) ###k
        """
        copy an expr or instance to make a new expr with the same formulas [experimental]
        """
        return self._copy(_instance_warning = False)
    def _destructive_copy(self, old):
        """
        [private]
        Modify self to be a copy of old, an expr of the same class.
        """
        assert not self._e_is_instance
        # self has to own its own mutable copies of the expr data in old, or own objects which inherit from them on demand.
        self._e_kws = dict(old._e_kws) ###k too simple, but might work at first; make it a FormulaSet object??
        self._e_args = old._e_args # not mutable, needn't copy; always present even if not self._e_has_args (for convenience, here & in replace)
        self._e_has_args = old._e_has_args
        return

    # common private submethods of __init__ and __call__
    def _destructive_init(self, args, kws):
        """
        [private, called by __init__ or indirectly by __call__]
        Modify self to give it optional args and optional ordinary keyword args.
        """
        assert not self._e_is_instance
        if kws:
            self._destructive_customize(kws)
        if args or not kws:
            self._destructive_supply_args(args)
        return
    def _destructive_customize(self, kws):
        """
        [private]
        Destructively modify self, an expr, customizing it with the formulas represented by the given keyword arguments.
        """
        assert not self._e_is_instance
        # self._e_kws dict is owned by every non-instance customized expr (inefficient?)
        # but it's shared by Instances and their expr templates
        # don't do this, since we need canon_expr:
        ## self._e_kws.update(kws)
        for k,v in kws.iteritems():
            self._e_kws[k] = canon_expr(v)
        return
    def _destructive_supply_args(self, args):
        """[private]
        Destructively modify self, an expr, supplying it with the given argument formulas.
        """
        assert not self._e_is_instance
        assert not self._e_has_args, "not permitted to re-supply already supplied positional args, in %r" % self # added message 061126
        self._e_has_args = True # needed in case args are supplied as ()

        args = tuple(map(canon_expr, args))
        self._e_args = args

        # also store the args in equivalent options, according to self._args declaration
        # Q: Should we do this on instantiation instead?
        # A: Probably not -- if you supply an option by positional arg, then later (i.e. in a later __call__) by named option,
        # maybe it should work (as it does now in the following code), or maybe it should be an error,
        # but it probably shouldn't be silently ignored.
        #  Note: right now, since internal & public options are not distinguished, you could do funny things like
        # customize the value of _args itself! That might even make sense, if documented.
        _args = getattr(self, '_args', None) #e make a class attr default for _args
        if _args and 'kluge 061113':
            if self._e_is_symbolic:
                # as of 061113 late: happens to _this when constructing _this(class) (I think) --
                # not yet sure whether/how to fix it in SymbolicExpr.__getattr__,
                # so fix it like this for now...
                # as of 061114 I think it will never happen, so also assert 0.
                printnim("better fix for _args when _e_is_symbolic??")##e
                assert 0 # see above comment
                _args = None
        if _args:
            printfyi( "_args is deprecated, but supported for now; in a pyinstance of %r it's %r" % (self.__class__, _args) )#061106
            if type(_args) is type(""):
                # permit this special case for convenience
                _args = (_args,)
            if not (type(_args) is type(())):
                # note: printed only once per value of _args (which might be lots of times, but not as many as for each error)
                printnim( "_args should have type(()) but doesn't; its value is %r" % (_args,) )
                    # this once happened since I said ('thing') when I meant ('thing',). Should the first case be allowed,
                    # as a special case for convenience? Probably yes, since the values have to be strings.
                    # We could even canonicalize it here.
            ###e also extend the decl format for equiv of * or **, and rename it _argnames or something else (see small paper note)
            args = self._e_args
            argnames = _args
            for i in range(min(len(argnames), len(args))):
                name = argnames[i]
                val = args[i]
                assert type(name) is type("")
                self._e_kws[name] = val
                continue
            pass
        
        # Q. When do we fill in defaults for missing args? A. We don't -- the above code + options code effectively handles that,
        # PROVIDED we access them by name, not by position in self._e_args. (Is it reasonable to require that?? ###k)
        # Q. When do we type-coerce all args? For now, I guess we'll wait til instantiation. [And it's nim.]
        # [later 061106: handled by Arg macro.]
        printnim("type coercion of args & optvals")
        printnim("provision for multiple arglists,")
        printnim("* or ** argdecls,")
        printnim("checking for optnames being legal, or not internal attrnames")
        # Note: this scheme needs some modification once we have exprs that can accept multiple arglists...
        # one way would be for the above assert [which one? I guess the not self._e_has_args]
        # to change to an if, which stashed the old args somewhere else,
        # and made sure to ask instantiation if that was ok; but better is to have a typedecl, checked now, which knows if it is. ###@@@
        return # from _destructive_supply_args

    # instantiation methods
    def _e_make_in(self, env, ipath):
        """
        Instantiate self in env, at the given index-path [by default] [or return an existing instance at that ipath??].
        [Note [semi-obs]: as of some time before 061110, this is usually called via _e_eval;
         and as of 061110 it's probably always called that way;
         probably they'll be merged at some point, unless some subtle difference
         is discovered (e.g. related to finding memoized instances).]
        [Later note, 070115: soon the "eval reform" will mean this is no longer called by _e_eval.]
        """
        ##print "_e_make_in%r" % ((self, env, ipath),) #k is this ever called? [061215 Q] -- yes, lots of times in a single test.
        # of course it runs, _e_eval calls it... which will change when we do the eval/instantiate reform, maybe soon. [070112]
        assert env #061110
        # no need to copy the formulas or args, since they're shared among all instances, so don't call self._copy. [###k still true?]
        # instead, make a new instance in a similar way.
        printnim("_e_make_in needs to replace _self with env._self in our externally-sourced formulas") #####@@@@@ 061110
            # or could it do this when running them, eg by having a different env to run them in? not sure, they might get further
            # modified when we get customized... but my guess is this would work... come to think of it, the env they need is the
            # one passed here, and what goes wrong is that we run them in a modified env with new self stored as _self,
            # btu that's only appropriate for the built-in formulas, not the passed ones, for which the passed env is the best.
            # but we have some formulas that combine builtin & passed parts...
            # should we just wrap these outside formulas by something to drop one layer from env,
            # knowing we always modify it by adding _self? Better would just be something to replace env.
            # Note I could store them shared, and wrap them as I retrieve them, I think -- in _i_grabarg.
            # BTW is it desirable for everything in env, or just _self? what about ipath? #####@@@@@
        ## assert not self._e_is_instance
        if self._e_is_instance:
            # new feature 070106, experimental but seems sensible/principled (since same as for constants like pyobjs) and desirable;
            # I'm sure about ignoring ipath but not completely sure about ignoring env, but it's my best guess,
            # and I don't know how *not* to ignore it. If you want a shared instance, this does it (see testexpr_26 for a demo
            # and some discussion); if you wanted mostly-shared but different instances, try something fancier.
            ## print "fyi: instantiating an instance returns it unchanged: %r" % self # works fine, no need to print for now
                # Q: Will we want to print this once per created main instance in which it happens, to warn about accidental use
                #    of this new feature?
                # A: I doubt we need to, since in a long time when this was an assertfail, I never saw it until I wanted to
                #    draw one instance twice (in testexpr_26).
            # [update 070117 re EVAL_REFORM -- we might do this in a caller, env.make, and perhaps make that the only caller,
            #  so it has the same noop special case for "numbers used as exprs" too ####e]
            if EVAL_REFORM:
                print("_e_make_in called on Instance even after EVAL_REFORM -- shouldn't widget_env.make or _i_instance intercept that?")
                #### in fact, _i_instance should intercept it, so it ought to never happen, so use print not printfyi. 070117
                return self
            else:
                return self
        ####e 070115: Here is where I think we need to ask self to decide what ipath a new instance should have,
        # and then if an old one is there and not out of date (which may involve comparing its init-expr to self??), return it.
        # (We may want to optimize the expr-compare by interning those exprs as we work. Maybe one could point to the other as equal?
        #  Is there any way that can optim the discovery that they're *not* equal, as full interning can? #e)
        # Alternatively, maybe the caller should ask about this first (perhaps using a larger containing expr)? No, I doubt it.
        printnim("optim (important): decide what ipath a new instance should have, and if a valid old one is there, return it")#070115
        return self.__class__(_make_in = (self,env,ipath)) # this calls _destructive_make_in on the new instance
            # note: if you accidentally define __init__ in your IorE subclass, instead of having it get its args using Arg etc,
            # you might get an exception here something like "TypeError: __init__ got an unexpected keyword argument _make_in". [070314]
    def _destructive_make_in(self, data):
        """
        [private]
        This is the main internal instantiation-helper method.
        For expr, env, ipath = data, modify self (which initially knows nothing)
        to be an instance of expr, in the given env, at the given index-path.
        Called only from __init__, when self knows nothing except its class.
        """
        ## printnim("make_in needs to replace _self in customization formulas with _self from env")#061110 313p
            # 061113 Q: what's the status of this? does _e_eval or _e_compute_method do it somehow, making this obs?? 
            # or is it truly nim -- but if so, how does _self work in args inside _value in Boxed? oh, that's not customized,
            # rel to Boxed -- but it is, rel to the exprs in _value, which is what counts. hmm...
            # now I'm recalling something about lexenv_Expr fixing this (& it's dated later same day, 061110)...
            # yes, that fixes it inside grabarg; I am guessing it can be optimized to once per instance once we have
            # simplify to do that to most of grabarg; that scheme means instances of custom exprs can share formulas.
            # A: so I'll comment out this printnim.
            # Q: where & how should we replace _this(class) with the lexically innermost object of that class? ####@@@@
            # Can it be computed at runtime, so we can turn it into a macro? Does the debug code to print selfs of outer envs
            # do the right thing, or would that be too dynamic? Do we *want* it to be dynamic, so we can use it when writing
            # display mode customizations to refer to things in which our instances will be contained? If so, do instances
            # know their parents?
            # For initial uses, we might not care about these "details"! Possible initial kluge implem: let _this(class)
            # turn into an instance itself, which figures out at runtime what to delegate to. Doing this below, "class _this".
            # WRONG, next day 061114 I'm doing it differently: _this(class) turns into an internal_Expr. Still found as "class _this".
            
        printnim("should make_in worry about finding an existing instance at the same index?")
            # guess: no, it'd be obsolete; not sure. #061110 313p
            # update 070120: our caller _make_in wants to handle this -- by this time, a new self exists so it's too late or wasteful.
        
        expr, env, ipath = data ###@@@ might want to split env into rules (incl lexenv) & place (incl staterefs, glpane)
        assert env #061110
        assert not self._e_is_instance #e remove when works
        assert not expr._e_is_instance

        # copy all pure-expr things [order of several sections here revised, 070120]
        
        self._e_has_args = expr._e_has_args #k ??
        # copy references to _e_args & _e_kws
        # note: exprs passed to specific args or kws can be lazily type-coerced and instantiated by Arg or Option decls in subclasses
        self._e_args = expr._e_args
        self._e_kws = expr._e_kws # replaces _e_formula_dict as of 061106; WARNING: shared and mutable; WE MUST NEVER MODIFY IT

        # do pure-expr things that could (in theory) still leave us a pure expr, but are needed before instantiation
        
        ## assert expr._e_has_args, "this expr needs its arguments supplied: %r" % self
            # we might allow exceptions to this later, based on type decl, especially if it has no declared args
            # (how can we tell, here?? ###e),
            # tho there is then an ambiguity about whether you're only customizing it or not,
            # but OTOH that is tolerable if it takes no args, actually that's unclear if instantiation does something like
            # make a random choice! So we might want to detect that, warn at compile time, or so....
        if not expr._e_has_args:
            # try testexpr_9fx6. See comments near that test.
            ### update 070120: here is how I'd fix this, and propose to do it as soon as it won't confound other debugging/testing:
            # print "warning: this expr will get 0 arguments supplied implicitly: %r" % self ### remove when works [why no serno yet??]
            # 070323 -- removed the warning, since if it's ever an error, you'll find out when the missing args are accessed;
            # so far it's almost always been legit, so it's just been distracting.
            self._destructive_supply_args( () ) # supply a 0-tuple of args
                ##e in case having zero args is illegal and this [someday] detects it,
                # we should pass it a flag saying to use different error msgs in this "implicit supply args" case. (implicit = True)
                # WARNING: make sure we copied all expr stuff but set no Instance stuff before doing this!
                # And make sure the "destructive" here can't mess up expr._e_args which we share.
                # Maybe that flag we pass should also warn it not to destroy anything else (e.g. if it someday feels like
                # adding a generated keyword arg, it better copy _e_kws in this case). ##e
            assert self._e_has_args
        
        # set some Instance things
        
        self._e_class = expr # for access to _e_kws and args #k needed? #e rename: self._e_expr? not sure. #k not yet used

        printnim("instance_helpers needs init_class call, init_expr call")
        #e call _init_class and/or _init_expr if needed [here or more likely earlier]

        #semi-obs cmts:
        ### AND have some way to get defaults from env
        ### AND take care of rules in env -- now is the time to decide this is not the right implem class -- or did caller do that?
        ### and did caller perhaps also handle adding of type coercers, using our decl??

        # 061020 hopes [rejargoned but not updated, 061106]:
        # maybe we can work for all, incl OpExprs & iterators, by only setting up RULES to instantiate,
        # which if not used, never happen. self.kids.<index/attr path> has to come from some self._e_args or self._e_kws member...
        # index always comes from <index/attr path> and *not* from which arg or opt, i think... sometimes they correspond,
        # and some predeclared members of kids could access those for you, e.g. self.kids.args or just self.args for short...
        # we could even say _e_args for the raw ones, .args for the cooked ones.
        
        self._e_is_instance = True

        self._init_self_as_Instance(env, ipath)
        
        return # from _destructive_make_in
    
    def _init_class(self): ###@@@ CALL ME
        """
        called once per directly-instantiated python class, when its first python instance is created
        [subclasses should replace this]
        """
        pass
    def _init_expr(self): ###@@@ CALL ME
        """
        called once per Expr when it gets its args [details unclear, maybe not yet needed]
        [subclasses should replace this]
        """
        pass

    def _e_eval(self, env, ipath): # implem/behavior totally revised, late-061109; works - not sure it always will tho... ###k
        # handling of If: As of 061212 If_expr (a subclass of this) overrides _e_eval and seems to work; see comments there.
        # handling of _value: done at a higher level only -- see InstanceMacro.
        if EVAL_REFORM: #070117
            ## assert not self._e_is_instance, "EVAL_REFORM means we should not eval an instance: %r" % self
            if self._e_is_instance:
                # newly allowed 070122 for sake of _26g and _19g tests -- works
                # [guess: this would not be needed if canon_expr wrapped Instances; not confirmed, but why make it bother doing that?]
                # print "fyi: instance %r evals to itself after EVAL_REFORM" % self # remove if this is deemed fully ok and normal
                return self
            # exprs that need instantiation eval to themselves. ###k will there be exceptions that are subclasses of this?
            ## printfyi("probably normal: eval to self of a subclass of IorE: %s" % self.__class__.__name__) # find out which classes this happens to ###
            return self._e_eval_to_expr(env, ipath, self) ###e call from other _e_eval defs ??
                # not just "return self", in case we need to wrap it with a local-ipath-modifying expr
                # Q: will the point of the local ipath be eval to any pure expr wanting instantiation in future, ie "free in ipath",
                # not just eval to self? A: I think so.
        else:
            return self._e_make_in(env, ipath)
        pass

    # ==
    
    def _C__delegate(self): #070121
        delegate = self.delegate
        if is_pure_expr(delegate):
            # instantiate it, as if it was wrapped with Instance -- do exactly what Instance would do
            delegate = self._i_instance( delegate, 'delegate')
                # is that the same index Instance would use? I think so (it gets passed _E_ATTR which should get replaced with this).
        return delegate

    # ===

    def drawkid(self, kid): # note: supersedes nim _e_decorate_draw [070210]
        #e plans: debug_pref for no-net-coord-change enforcement (for testing),
        # and most importantly, make possible a drawing pass which draws only a specified subset of drawables
        # but in their coord- or glstate- transforming containers, e.g. for a highlight-drawing pass.
        #
        # Update 070414:
        #    To accomplish that, we need to record a kid->parent map during draw (transient per-frame) or before it;
        # for kids with multiple parents, we hope (in evolving a simple API for this) that the parent-chains
        # have different glname-chains unless both kid copies can be co-highlighted;
        # for kids drawn twice by one parent (e.g. a stereo-viewer or a parent which does some sort of draw/overdraw scheme),
        # we'd probably like that parent to wrap each one differently, so no kid is really drawn twice by one parent,
        # and so the wrapper can give each one a different glname if necessary, or tell one not to record drawing-coords
        # (or equiv drawing-parent-chain info) for highlighting.
        #    For a prototype of related code from 070219 or earlier, see a scratch file, rendering.py.
        if kid is not None:
            try:
                if is_expr_Instance(kid):
                    kid.draw()
                else:
                    print "***BUG: drawkid in %r sees non-Instance (skipping it): %r" % (self, kid,)
                        #070226, worrying about self.delegate rather than self._delegate being passed --
                        # common but why does it work??####BUG?? in testexpr_33x I tried it and it fails here...
                        # I predict there are a bunch of bugs like this, that some used to work, and others were not tested
                        # since drawkid came in, and in others self.delegate happens to be an Instance (maybe not true bugs then,
                        # but unclear). Hmm, if delegate = Arg() then it is always an Instance -- a style decision is needed for that --
                        # it should be "always use _delegate" even then I think, otherwise the code is a bad example.
                        ###FIX all calls of drawkid
            except:
                print_compact_traceback("bug: exception in %r.drawkid(%r): " % (self, kid))
        return

    # == methods moved from class Widget (which is being merged into this one for now -- merge is not complete, it exists as an
    #  empty shell ###fix) [070201]

    # note: this is a method in case color is in local coords, BUT that won't work properly if subclass ought to delegate
    # this -- this def means it won't. Still, for now keep it here since implem never changes. Later clean up delegation for this
    # as discussed elsewhere. ###fix [070201]
    def fix_color(self, color): #e move to glpane??
        """
        Return the given color, suitably formatted for passing to low-level drawing code for the current drawing medium.
        The color is assumed to be evaluated (i.e. no longer a formula), but perhaps in some general data format
        and perhaps needing to be subjected to global color transformations stored transiently in the drawing medium object
        (e.g. a color warp or constant alpha, stored in the glpane).
        [#e If it proves unworkable for this method to know the current drawing medium,
         it will probably be replaced with more specific methods, eg fix_color_GL and fix_color_POVRay.]
        [#e Will we have to have variants of _GL for returning 3 tuples or 4 tuples as the color?]
        [#e Will we have to worry about material properties?]
        """
        #e first decode it and normalize it as needed
        color_orig = color # for error messages
        color = normalize_color(color)
        warpfuncs = getattr(self.env.glpane, '_exprs__warpfuncs', None) #070215 new feature
        while warpfuncs:
            func, warpfuncs = warpfuncs # pop next func from nested list
            try:
                # do color = normalize_color(func(color)), but don't change color if anything goes wrong
                color0 = '?' # for error message in case of exception
                color0 = func(color) # don't alter color itself until we're sure we had no exception in this try clause
                color = normalize_color(color0) # do this here (not in next while loop body), since errors in it are func's fault
            except:
                print_compact_traceback("exception in %r.fix_color(%r) warping color %r with func %r (color0 = %r): " % \
                                        ( self, color_orig, color, func, color0 ) )
                pass
            continue
        return color
            ###e guess: it comes from the glpane as a method on it. the glpane is in our env.
            # self.env.glpane.fix_color
        ### fix_color is a method on self, I guess, or maybe on env (and env may either be self.env or an arg to this method -- guess, arg)
        # or maybe it has contribs from env, class, and glpane -- glpane to know what kind of color it needs,
        # env or glpane to know about warps or alphas to apply, class maybe, and something to know about resolving formulas (env?)
        ### we'll have fix_ for the dims too, handling their units, perhaps in a way specific to this class
        ### we'll have memoization code for all these attrs
        ### and we'll need better control of gl state like GL_CULL_FACE, if we can run this while it's already disabled

    # note: draw can't be defined here since it masks the one provided by the delegate, if the subclass delegates! [070201]    
##    def draw(self):
##        "#doc"
##        print "warning: no draw method in %r" % self # probably verbose enough to not be missed...
##        return

    def KLUGE_gl_update(self): #070213
        """
        ###KLUGE: call our glpane.gl_update explicitly. Should never be needed (unless there are bugs),
        since any change that affects what we draw should be changed-tracked when made, and usage-tracked when some
        draw method (used directly or to make displist contents) uses it. But evidently there are such bugs [as of 070213],
        since some things apparently need to call this.
           Note: all calls of gl_update should go through this method, if possible,
        if they're needed due to changes in attrs of self.
        """
        self.env.glpane.gl_update()
        return
    
    pass # end of class InstanceOrExpr

# ==

class InstanceHolder: #070414
    """
    A self-contained place to make and hold Instances of exprs for use with a given glpane,
    which makes its own drawing env and (by default) its own state and initial ipath,
    and which caches the Instances at specified indices (using the API of InstanceOrExpr.Instance for details).
    """
    def __init__(self, glpane, state = None, ipath = None):
        if state is None:
            state = {}
        if ipath is None:
            ipath = 'InstanceHolder-Null-ipath'
        self._autoindex = 0
        self.ipath = ipath
        self.env = widget_env( glpane, state)
        self._thing = self.env.make( InstanceOrExpr(), self.ipath ) #k will this expr work?
            # make this exactly once;
            # its only purpose is to cache instances and provide the .Instance API
    def Instance(self, expr, index = None, **kws):
        """
        make instances in self, using API similar to IorE.Instance.

        @index: if not provided or None, make up a new unique index for the expr;
                otherwise use the provided index (which if not unique will cause
                a prior returned Instance to be reused for a new expr or replaced
                with one made from it, depending on other options).
        """
        if index is None:
            #bruce 080528 new feature
            self._autoindex += 1
            index = ('__autoindex__', self._autoindex)
        # note (digression): index is not allowed to contain objects like Atoms,
        # due to restrictions in other code about ipath -- should be fixed.
        # [bruce 080528 comment]
        return self._thing.Instance(expr, index, **kws)
    pass

def get_glpane_InstanceHolder(glpane): #070414
    """
    Find or make a central place to store cached expr Instances associated with a given glpane.

    @warning: there is one such place per glpane, with only one namespace
              for expr indices, effectively shared throughout the entire app.
    """
    try:
        place = glpane._exprs__InstanceHolder
        #e could decide whether we need to remake it for some reason, e.g. code-reload
    except AttributeError:
        place = glpane._exprs__InstanceHolder = InstanceHolder(glpane)
    return place

# ===

_DELEGATION_DEBUG_ATTR = '' # you can set this to an attrname of interest, at runtime, for debugging

class DelegatingMixin(object): # 061109 # see also DelegatingInstanceOrExpr #070121 renamed delegate -> _delegate, let IorE relate them
    """
    #doc: like Delegator, but only legal in subclasses which also inherit from InstanceOrExpr;
    other differences from Delegator:
    - we delegate to self._delegate rather than self.delegate
    - we don't delegate any attrs that start with '_'
    - no caching, in case the delegate varies over time
    - self._delegate should be defined by the subclass rather than being an instance variable of this class
      (e.g. it might be recomputable from a formula or _C__delegate compute method handled by ExprsMeta,
       or assigned in __init__)
    - we only support subclasses which also inherit from InstanceOrExpr
      - note: they should include InstanceOrExpr before DelegatingMixin in their list of base classes
      - note: InstanceOrExpr defines a compute method for self._delegate from self.delegate (which instantiates it if needed)
    - we don't start delegating until self is an Instance; before that we print a warning to say it was tried. (#k is it an error??)
      [##e This is why we don't support use by non-IorE subclasses. Maybe we could generalize this class to use a more general way
       of asking the pyinstance whether to delegate yet, like some other optional flag attr or method.
       (But probably not just by testing self._delegate is None, since the point is to avoid running a compute method
        for self._delegate too early -- in IorE subclasses this would cause an error when they're a pure expr.)]
    """
    # Note: DelegatingMixin has always appeared after IorE (not before it) in the list of base classes.
    # I think this order can't matter, since it only defines __getattr__, which is not otherwise defined in any IorE
    # subclass or superclass, and DelegatingMixin is only legal in IorE (and I just now verified it's only used there, 070206).
    # The only "close calls" are that __getattr__ is defined in SymbolicExpr, and some obsolete code said
    #   "class _this(SymbolicInstanceOrExpr, DelegatingMixin)".
    # So if we wanted to require DelegatingMixin to come first (so it could override things defined in IorE
    # or its subclasses (like the experimental _i_type, if it survives)), we could probably make that change.

    ##e optim: do this instead of the assert about attr: _delegate = None # safer default -- prevent infrecur
    def __getattr__(self, attr): # in class DelegatingMixin
        try:
            return super(DelegatingMixin,self).__getattr__(attr)###k need to verify super args in python docs, and lack of self in __getattr__
                # note, _C__attr for _attr starting with _ is permitted, so we can't check whether attr starts '_' before doing this.
        except LvalError_ValueIsUnset:
            # 061205 new feature to prevent bugs: don't delegate in this case. UNTESTED! #####TEST
            print "fyi (maybe not always bug): " \
                  "don't delegate attr %r from %r when it raises LvalError_ValueIsUnset! reraising instead." % (attr, self)
            raise
        except AttributeError:
            if attr.startswith('_'):
# useful debug code -- commented out, but keep around for now [061110]:
##                if not attr.startswith('__'):
##                    # additional test hasattr(self._delegate, attr) doesn't work: AssertionError: compute method asked for on non-Instance
##                    # we need that test but I don't know how to add it easily. __dict__ won't work, the attr might be defined on any class.
##                    # maybe look in __dict__ of both self and its class?? #e
##                    print "warning: not delegating missing attr %r in %r (don't know if defined in delegate)" % \
##                          (attr, self)#061110 - have to leave out ', tho it's defined in delegate %r'
##                        # before hasattr check, this happens for _args, _e_override_replace, _CK__i_instance_CVdict
##                        # (in notyetworking Boxed test, 061110 142p).
##                        #e could revise to only report if present in delegate... hard to do, see above.
                raise AttributeError, attr # not just an optim -- we don't want to delegate any attrs that start with '_'.
                ##k reviewing this 061109, I'm not sure this is viable; maybe we'll need to exclude only __ or _i_ or _e_,
                # or maybe even some of those need delegation sometimes -- we'll see.
                #e Maybe the subclass will need to declare what attrs we exclude, or what _attrs we include!
            assert attr != 'delegate', "DelegatingMixin refuses to delegate self.delegate (which would infrecur) in %r -- " \
                   "does its class lack a definition of delegate?" % self
                # that assert makes sense even though our low-level delegate is now self._delegate. [070121]
            if expr_is_Instance(self):
                recursing = self.__dict__.setdefault('__delegating_now', False) # note: would be name-mangled if set normally
                assert not recursing, "recursion in self._delegate computation in %r" % self #061121
                self.__dict__['__delegating_now'] = True
                try:
                    delegate = self._delegate
                finally:
                    self.__dict__['__delegating_now'] = False #e or could del it, presumed less efficient, not sure
                ##e could check that delegate is not None, is an Instance (??), etc (could print it if not)
                #k Should it be silently tolerated if delegate is None? Probably no need -- None won't usually have the attr,
                # so it will raise the same exception we would. Are there confusing cases where None *does* have the attr??
                # I doubt it (since we're excluding _attrs). But it's worth giving more info about likely errors, so I'm printing some.
                if delegate is None:
                    raise AttributeError, attr # new feature 070121: this means there is no delegate (more useful than a delegate of None).
                if is_pure_expr(delegate):
                    print_compact_stack( "likely-invalid _delegate %r for %r in self = %r: " % (delegate, attr, self)) #061114
                if attr == _DELEGATION_DEBUG_ATTR: # you can set that to any attr of current interest, for debugging
                    print "debug_attr: delegating %r from %r to %r" % (attr, self, delegate)
                try:
                    res = getattr(delegate, attr) # here is where we delegate. It's normal for this to raise AttributeError (I think).
                except AttributeError:
                    #### catching this is too expensive for routine use (since I think it's often legitimate and maybe common -- not sure),
                    # but it's useful for debugging -- so leave it in for now. 061114
                    printnim("too expensive for routine use")
                    msg = "no attr %r in delegate %r of self = %r" % (attr, delegate, self)
                    if attr == _DELEGATION_DEBUG_ATTR: 
                        print "debug_attr:", msg
                    raise AttributeError, msg
                else:
                    if attr == _DELEGATION_DEBUG_ATTR:
                        print "debug_attr: delegation of %r from %r is returning %r" % (attr, self, res) #070208
                    return res
            print "DelegatingMixin: too early to delegate %r from %r, which is still a pure Expr" % (attr, self)
                # it might be an error to try computing self._delegate this early, so don't print it even if you can compute it
                # (don't even try, in case it runs a compute method too early)
            raise AttributeError, attr
        pass
    pass # end of class DelegatingMixin

# ==

# Implem notes for _value [061110, written when this was in Boxed.py]:
# - Q: make it have an implicit Instance, I think -- or use a convention of adding one explicitly?
#   A: the convention, so it can work with other formulas too? no, they'd also need instantiation to work... not sure actually.###@@@
#   - BTW that might mean we get in trouble trying to pure-eval (which is what?) an Expr made from InstanceOrExpr, e.g. for a formula
#   to figure an iterator-expr to use in something else! We'll have to try that, and understand the semantics more clearly...
#   could it be that we'll have to explicitly Hold the exprs assigned to class attrs in cases like that?!?
# - where we use that _value is probably in _e_eval in InstanceOrExpr. (I still don't know if that and make_in are exactly the same.)
# - so we could do the instantiation in the place where we use it... then (later) test what happens if it's a number or so. (##e)
#   - (or what if the entire macro with its value is supposed to come up with a pure expr? test that too. ##e)
# So for now, just run the instantiation in the place we use it, in InstanceOrExpr._e_eval.
# But worry about whether InstanceOrExpr._e_make_in sometimes gets called instead by the calling code.
# Maybe better do it in whichever one has precedence when they both exist... that's _e_make_in (used in _CV__i_instance_CVdict).
# _e_eval just calls it, so that way it'll work in both. Ok, do it. ###
# WAIT, is it possible for the _value formula to vary with time, so that we want an outer instance,
# delegating to a variable inner one? (Perhaps memoizing them all sort of like GlueCodeMemoizer would do... perhaps only last one.)
# hmm... #### note that _make_in should pick up the same instance anyway, if it exists, but it looks to me like it might not!!! ###BUG
#
# let's try an explicit experiment, InstanceMacro:

class InstanceMacro(InstanceOrExpr, DelegatingMixin): # ca. 061110; docstring revised 061127 & 070117; see also DelegatingInstanceOrExpr
    """
    Superclass for "macros" -- they should define a formula for _value which they should always look like.
    # WARNING: a defect in InstanceMacro means that "default defs" in its client class (e.g. thing, ww in Boxed),
    # or in a superclass of that, will override their defs in _value rather than being delegated to _value
    # (since they are already defined, so they're never seen by DelegatingMixin.__getattr__);
    # so be careful what you name those local defs, and what default values are defined in a superclass!
    #    This is also sometimes a feature, especially when the local defs occur in the same class that defines _value
    # (permitting you to override some of the otherwise-delegated attrs, e.g. bright or width),
    # tho at one time I thought delegate should let you do that, _value should not,
    # and this would be a useful distinction. I don't yet know how to make _value not do it,
    # nor whether that's desirable if possible, nor whether it's well-defined. ###@@@ [061114]
    #    It means, for example, that the InstanceMacro Boxed would not work if it inherited from Widget2D,
    # since it would pick up Widget2D default values for lbox attrs like btop. [unconfirmed but likely; 061127 comment]
    """
    #e might not work together with use by the macro of DelegatingMixin in its own way, if that's even possible [later: not sure what this comment means]
    if EVAL_REFORM:
        printnim("is this eval_Expr correct or not? does it even make any difference??")
        ## delegate = Instance( eval_Expr(_self._value), '!_value') #### guess about fixing testexpr_5 bug; didn't make any clear difference [070117]
        delegate = Instance( _self._value, '!_value') # innocent until proven guilty, even if suspected (more seriously, don't change what might not be wrong)
        # Note: as of 070121 late, due to changes in IorE, this explicit Instance() should no longer be needed (when EVAL_REFORM or not).
        # But that's only been tested in other classes and when EVAL_REFORM.
    else:
        # old code, worked for a long time
        delegate = Instance( _self._value, '!_value') #k guess: this might eval it once too many times... ####k
        # [later, as of 061113 -- it works, but this point of too many evals has never been fully understood,
        #  so for all i now, it happens but causes no obvious harm in these examples -- should check sometime. ##e]
        
        # [much later, 061114: i think I know why it works -- Instance indeed does an eval, but it's the eval from _self._value
        #  to what's stored in self._value, which is *already* an Instance because evaling the rule makes one!
        #  see other comments about this in Boxed _init_instance dated today. as they say, it's all wrong and will probably change.
        #  in fact, if I said here delegate = _self._value, I think it ought to work fine! Try it sometime. ######TRYIT]
        
        #k Note: I used '!_value' as index, because I worried that using '_value' itself could collide with an index used to eval the expr version of _value,
        # in future examples which do that (though varying expr is not presently supported by Instance, I think -- OTOH
        # there can be two evals inside _i_instance due to eval_Expr, so the problem might arise that way, dep on what it does with
        # indices itself).
        
    ##e could add sanity check that self.delegate and self._value are instances
    # (probably the same one, tho not intended by orig code, and won't remain true when Instance is fixed) [061114]
    def _e_model_type_you_make(self): ###k 070215 very experimental
        "#doc; overrides super version; see variant in DelegatingInstanceOrExpr"
        # assume we are a graphical DIorE (like a model obj decorator -- Overlay, Translate, DraggableObject...), so we do delegate it...
        # but wait, is code for this different if we're expr or instance? instance could go to _delegate, expr to _value
        if self._e_is_instance:
            return self._delegate._e_model_type_you_make() ###e check self._delegate not None??
        else:
            assert is_pure_expr(self._value)
            return self._value._e_model_type_you_make()
        pass
    pass # end of class InstanceMacro

# ==

class DelegatingInstanceOrExpr(InstanceOrExpr, DelegatingMixin): # moved here & basic 061211, created a few days before
    """
    #doc
    """
    #e this might replace most uses of DelegatingMixin and InstanceMacro, if I like it

    def _e_model_type_you_make(self): ###k 070215 very experimental
        "#doc; overrides super version; see variant in InstanceMacro"
        # assume we are a graphical DIorE (like a model obj decorator -- Overlay, Translate, DraggableObject...), so we do delegate it...
        # but wait, is code for this different if we're expr or instance? instance could go to _delegate, expr to delegate
        if self._e_is_instance:
            return self._delegate._e_model_type_you_make() ###e check self._delegate not None??
        else:
            assert is_pure_expr(self.delegate) # can this fail for glue-code-objs or helper-objs like the ones MT_try2 makes? ##k
            return self.delegate._e_model_type_you_make()
        pass
    pass

# new unfinished code, not sure useful, commented out, goes with other such code from today related to _i_type [070206]
##class InstanceWrapper(DelegatingInstanceOrExpr): #070206 ###e rename??? not yet used. see below for analysis of who should use this.
##    """[proposed] The standard superclass for an InstanceOrExpr which delegates to another one
##    (an instance of an expr defined by each subclass as self.delegate, which can be time-varying)
##    in such a way as to "wrap" or "decorate" it without changing its type.
##       (Note: It's possible that the implem of delegation in all such cases will be changed someday
##    so that self and its delegate are the same object, only differing in unbound method functions. ###e)
##    """
##    _e_delegate_type = True
##    pass

# Should the following delegating IorE subclasses delegate their type? [070206 survey]
# Note, this only matters if they are fed into a model-object-container and wrapped around a model object --
# if that can ever happen legitimately, say yes -- or if they are used as model object even if they're a graphics primitive
# like Cylinder (really a geometric object, not just a graphics prim). So for Overlay we'll say yes, in case it wraps
# a model obj, even though that should probably not occur directly in a model obj container. Same with Highlighting, etc.
# We'll only say no for things that might look like model objects themselves (whose type a user might want to see as the
# type of object they put in the model). So far most of the code here is not a model obj, so maybe I should make the default yes...
#
# SpacerFor - no (it takes the size of a model object, but it surely does not take other aspects of its nature)
#   [###e this suggests that what's really going on is that we need to be saying what interfaces we do and don't delegate,
#    and SpacerFor is delegating layout but not other graphics and not model nature, which this "type" is part of --
#    really it's not just plain type, but "model object type".]
# Highlightable(image) - no
# Highlightable(ModelObject) - maybe yes, but looks deprecated -- Highlightable should be used in a viewing macro. So no.
# ImageChunk [not yet used] - like Highlightable? no... unclear. Maybe this model/graphics-object distinction needs formalization...
# Overlay - 
# ==

class ModelObject(DelegatingInstanceOrExpr): #070201 moved here from demo_drag.py; see also ModelObject3D, Geom3D...
    ###WARNING: as of 070401 it's unclear if the value of _e_model_type_you_make is used for anything except other implems
    # of the same method.
    """
    #doc -- class for datalike objects within the model
    [this may diverge from DelegatingInstanceOrExpr somehow -- or i might be mistaken that it needs to differ,
    but if so, it might have a better name! ###e]
    """
    # One way it might diverge -- in having delegation to env.viewer_for_model_object [nim];
    # something like this:
    ###e delegate = something from env.viewer_for_model_object(self) or so
    #070203 for now, superclass provides a default delegate of None, since some subclasses have one (which will override that)
    # and some don't, and without this, the ones that don't have an assertfail when they ought to have an AttributeError.
    delegate = None
    def _e_model_type_you_make(self): ###k 070215 very experimental
        """
        #doc; overrides super version
        """
        # but lots of specific DIorE don't delegate it...
        # model obj DIorE don't [that's us], but graphical ones do. For now assume we know by whether it's subclass of ModelObj.
        return InstanceOrExpr._e_model_type_you_make(self) # that implem has the right idea for us -- use classname
    pass

class WithModelType(DelegatingInstanceOrExpr): # 070215 experimental, ###UNTESTED (except for not damaging delegate when instantiated)
    """
    #doc better -- be like arg1, but with arg2 specifying the model type --
    note, arg2 has to be accessed even when we're an expr
    """
    delegate = Arg(InstanceOrExpr) #e first use of that as type -- ok in general? correct here?
    arg_for_model_type = Arg(str) #e wrong Arg type? #e have an option to say this Arg works even on exprs???? (very dubious idea)
    def _e_model_type_you_make(self): ###k 070215 very experimental
            # note: this would not be delegated even if overridden, I think, due to _e_ -- not sure! ####k
        if self._e_is_instance:
            return self.arg_for_model_type
        else:
            type_expr = self._e_args[1] # kluge? btw did this go thru canon_expr? yes...
            res = type_expr._e_constant_value ##k name ###e check for errors ##e use a helper function we have that does this (in If?)
            assert type(res) == type("") or res is None ##e remove when works, if we extend legal kinds of model_types
            return res
        pass
    pass

class WithAttributes(DelegatingInstanceOrExpr): # 070216 experimental, STUB
    """
    #doc better -- be like arg1, but with options specifying attrs you have
    (like customizations, but with new Option decls too)
    """
    delegate = Arg(InstanceOrExpr)
    def _init_instance(self):
        for k, v in self._e_kws.items():
            # we hope v is a constant_Expr
            ok, vv = expr_constant_value(v)
            assert ok, "WithAttributes only supports constant attribute values for now, not exprs like in %s = %r" % (k,v,)
            #e to support exprs, set up the same thing as if our class had k = Option(Anything) and v got passed in (as it did)
            # (note, vv is the eval of v, so we could just eval v here -- but need to wrap it in proper env...
            #  sometime see how much of needed work _i_grabarg already does for us -- maybe it's not hard to unstub this. ##e)
            setattr(self, k, vv)
        return
    pass

# ==

class _this(SymbolicExpr): # it needs to be symbolic to support automatic getattr_Expr
    """
    _this(class) refers to the instance of the innermost lexically
    enclosing expr with the same name(?) as class.
    """
    #### NOT YET REVIEWED FOR EVAL_REFORM 070117
    #k will replacement in _e_args be ok? at first it won't matter, I think.
    def __init__(self, clas):
        assert is_Expr_pyclass(clas) #k
        self._e_class = clas
        self._e_thisname = thisname_of_class( self._e_class)
        return
    #e __str__ ?
    def __repr__(self):
        return "<%s#%d%s: %r>" % (self.__class__.__name__, self._e_serno, self._e_repr_info(), self._e_class,)
    def _e_eval(self, env, ipath):
        dflt = None ## 'stub default for ' + self._e_thisname #####e fix
        res = env.lexval_of_symbolname( self._e_thisname, dflt )
            # I don't think we need to do more eval and thus pass ipath;
            # indeed, the value is an Instance but not necessarily an expr
            # (at least not except by coincidence of how _this is defined).
            # Caller/client could arrange another eval if it needed to. [061114 guess]
        assert res, "_this failed to find referent for %r" % self._e_thisname ##e improve
        return res
    pass # end of class _this   

    ##class _this(SymbolicInstanceOrExpr_obs, DelegatingMixin): #061113; might work for now, prob not ok in the long run
    #e [see an outtakes file, or cvs rev 1.57, for more of this obs code for _this, which might be useful someday]

# end