summaryrefslogtreecommitdiff
path: root/pypers/secret.txt
blob: 0288ed84be934d7e9152891778162f96ea52ab20 (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
ADVANCED METAPROGRAMMING TECHNIQUES
========================================================================

In elementary OOP, the programmer works with objects; in advanced OOP,
the programmer works with classes, taking full advantage of
(multiple) inheritance and metaclasses. Metaprograming is the activity of 
building, composing and modifying classes.

I will give various examples of metaprogramming techniques
using run-time class modifications
multiple inheritance, metaclasses, attribute descriptors and
even simple functions.

Moreover, I will show show metaclasses can change the
semantics of Python programs: hence theire reputation of *black* magic. 
That is to say that the techniques explained here are dangerous!

On code processing
--------------------------------------------------------------------

It is a good programming practice to avoid the direct modification
of source code. Nevertheless, there are situations where the ability of 
modifying the source code *dynamically* is invaluable. Python has the
capability of 

1) generating new code from scratch;

2) modifying pre-existing source code;

3) executing the newly created/modified code at run-time.

The capability of creating source code and executing it *immediately* has
no equivalent in static languages such as C/C++/Java and it is maybe the 
most poweful feature of dynamics languages such as Java/Python/Perl.
This feature has been exploited to its ultimate consequences in the languages
of the Lisp family, in which one can use incredibly poweful macros, which
in a broad sense, are programs that write themselves

In this chapter I will discuss how to implement macros in Python and I will
present some of the miracles you may perform with this technique. To this
aim, I will discuss various ways of manipulating Python source code, by
using regular expressions and state machines.


Regular expressions
-------------------------------------------------------------------------

 .. line-block::

  *Some people, when confronted with a problem, 
  think "I know, I'll use regular expressions." 
  Now they have two problems.*
     -- Jamie Zawinski


Python source code is a kind of text and can manipulated with the same
techniques that are used to manipulate text:

1. the trivial search and replace;

2. regular expressions;

3. state machines;

4. parser

There is not very much to say about the search and replace methods: it
is fast, efficient and it works. It should always be used whenever
possible. However, in this chapter I will only be interested in case
where one has to recur to something more sophisticated than a plain
search and replace. Something that can be managed with regular expression
or with something even more sophisticated than them: a state machine or
even a full featured parser.


I will *not* give a primer on regular expression here, since they are
already well documented in the standard documentation (see Andrew's
Kuchling 'Howto') as well in many books (for instance Mastering Regular
Expression and 'Python in a Nutshell'). Instead, I will give various
practical examples of usage.

  >>> import re
  >>> reobj=re.compile(r'x')


More on metaclasses and subclassing built-in types 
-------------------------------------------------------------------------

Subclassing ``list`` is easy since there are no methods returning lists
except the methods correspondings to the '+' and '*' operators. 
Subclassing ``str`` is more complicated, since one has many methods
that return strings. Nevertheless, it can be done with the ``AutoWrapped``
metaclass, simply by specifying the list of the builtins to be wrapped.

 ::

  #<oopp.py>

  class Str(str):
      __metaclass__=AutoWrapped
      builtinlist="""__add__ __mod__ __mul__ __rmod__ __rmul__ capitalize
          center expandtabs join ljust lower lstrip replace rjust rstrip strip
          swapcase title translate upper zfill""".split()

  #</oopp.py>
 
Here I show various tests.

  .. doctest

  >>> from oopp import Str
  >>> sum=Str('a')+Str('b') # check the sum
  >>> print sum, type(sum)
  ab <class 'oopp.Str'>
  >>> rprod=Str('a')*2 # check the right product 
  >>> print rprod,type(rprod)
  aa <class 'oopp.Str'>
  >>> lprod=2*Str('a') # check the left product 
  >>> print lprod,type(lprod)
  aa <class 'oopp.Str'>
  >>> r=Str('a').replace('a','b') # check replace
  >>> print r,type(r)
  b <class 'oopp.Str'>
  >>> r=Str('a').capitalize() # check capitalize
  >>> print r,type(r)
  A <class 'oopp.Str'>

``Str`` acts as a nice base class to built abstractions based on strings.
In particular, regular expressions can be built on top of strings describing
their representation (I remind that if ``x`` is a regular expression object, 
``x.pattern`` is its string representation). Then, the sum of two regular 
expressions ``x`` and ``y`` can be defined as the sum of their string 
representation, ``(x+y).pattern=x.pattern+y.pattern``. Moreover, it is
convenient to define the ``__or__`` method of two regular expression in
such a way that ``(x | y).pattern=x.pattern+'|'+y.pattern``.
All this can be achieved trough the following class:

 ::

  #<oopp.py>
 
  class BaseRegexp(Str):

      builtinlist=['__radd__', '__ror__']
      wraplist=['__add__','__or__']

      __add__ = lambda self,other: self.pattern + other 
      __or__  = lambda self,other: self.pattern+'|'+other

      def __init__ (self,regexp):
          "Adds to str methods the regexp methods"
          if '#' in regexp: 
              reobj=re.compile(regexp,re.VERBOSE)
          else: 
              reobj=re.compile(regexp) # non-verbose
          for attr in dir(reobj)+['pattern']:
              setattr(self,attr,getattr(reobj,attr))

  #</oopp.py>
 
  >>> from oopp import *
  >>> aob=BaseRegexp('a')|BaseRegexp('b'); print aob
  a|b
  >>> print pretty(attributes(aob))
  encode = <built-in method encode of BaseRegexp object at 0x401b25cc>
  endswith = <built-in method endswith of BaseRegexp object at 0x401b25cc>
  expandtabs = <function _ at 0x401b69cc>
  find = <built-in method find of BaseRegexp object at 0x401b25cc>
  findall = <built-in method findall of _sre.SRE_Pattern object at 0x4019b890>
  finditer = <built-in method finditer of _sre.SRE_Pattern object at 
  0x4019b890>
  index = <built-in method index of BaseRegexp object at 0x401b25cc>
  isalnum = <built-in method isalnum of BaseRegexp object at 0x401b25cc>
  isalpha = <built-in method isalpha of BaseRegexp object at 0x401b25cc>
  isdigit = <built-in method isdigit of BaseRegexp object at 0x401b25cc>
  islower = <built-in method islower of BaseRegexp object at 0x401b25cc>
  isspace = <built-in method isspace of BaseRegexp object at 0x401b25cc>
  istitle = <built-in method istitle of BaseRegexp object at 0x401b25cc>
  isupper = <built-in method isupper of BaseRegexp object at 0x401b25cc>
  join = <function _ at 0x401b6a74>
  ljust = <function _ at 0x401b6b1c>
  lower = <function _ at 0x401b6cdc>
  lstrip = <function _ at 0x401b6d84>
  match = <built-in method match of _sre.SRE_Pattern object at 0x4019b890>
  pattern = ba
  replace = <function _ at 0x401b6ed4>
  rfind = <built-in method rfind of BaseRegexp object at 0x401b25cc>
  rindex = <built-in method rindex of BaseRegexp object at 0x401b25cc>
  rjust = <function _ at 0x401ba0d4>
  rstrip = <function _ at 0x401ba10c>
  scanner = <built-in method scanner of _sre.SRE_Pattern object at 0x4019b890>
  search = <built-in method search of _sre.SRE_Pattern object at 0x4019b890>
  split = <built-in method split of _sre.SRE_Pattern object at 0x4019b890>
  splitlines = <built-in method splitlines of BaseRegexp object at 0x401b25cc>
  startswith = <built-in method startswith of BaseRegexp object at 0x401b25cc>
  strip = <function _ at 0x401ba25c>
  sub = <built-in method sub of _sre.SRE_Pattern object at 0x4019b890>
  subn = <built-in method subn of _sre.SRE_Pattern object at 0x4019b890>
  swapcase = <function _ at 0x401ba294>
  title = <function _ at 0x401ba4fc>
  translate = <function _ at 0x401ba534>
  upper = <function _ at 0x401ba5dc>
  wraplist = ['__add__', '__radd__', '__or__', '__ror__']
  zfill = <function _ at 0x401ba614>

  #<oopp.py>

  class Regexp(BaseRegexp):
      class __metaclass__(BaseRegexp.__metaclass__):
          def __setattr__(cls,name,value):
              if name==name.upper(): # all caps means regexp constant
                  if not isinstance(value,cls): value=cls(value)
                  value.name=name # set regexp name
              BaseRegexp.__metaclass__.__setattr__(cls,name,value)
              # basic setattr

      def __call__(self,name=None):
          "Convert the regexp to a named group with the right name"
          name=getattr(self,'name',name)
          if name is None: raise TypeError('Unnamed regular expression')
          return self.__class__('(?P<%s>%s)' % (name,self.pattern))

      generateblocks=generateblocks

  #</oopp.py>

The magic of ``Regexp.__metaclass__`` allows to generate a library of 
regular expressions in an elegant way:

 ::

  #<oopp.py>

  r=Regexp

  customize(r,
      DOTALL =r'(?s)' ,    # starts the DOTALL mode; must be at the beginning
      NAME   =r'\b[a-zA-Z_]\w*', # plain Python name
      EXTNAME=r'\b[a-zA-Z_][\w\.]*', # Python name with or without dots
      DOTNAME=r'\b[a-zA-Z_]\w*\.[\w\.]*',# Python name with dots
      COMMENT=r"#.*?(?=\n)",  # Python comment
      QUOTED1=r"'.+?'",       # single quoted string '
      QUOTED2=r'".+?"',       # single quoted string "
      TRIPLEQ1=r"'''.+?'''",  # triple quoted string '
      TRIPLEQ2=r'""".+?"""'   # triple quoted string " 
    )

  r.STRING=r.TRIPLEQ1|r.TRIPLEQ2|r.QUOTED1|r.QUOTED2
  r.CODESEP=r.DOTALL+r.COMMENT()|r.STRING()

  #</oopp.py>

The trick is in the redefinition of ``__setattr__``, which magically converts
all caps attributes in ``Regexp`` objects.

The features of ``Regexp`` can be tested with the following code:

 ::

  #<test_re.py>

  """This script looks at its own source code and extracts dotted names,
  i.e. names containing at least one dot, such as object.attribute or
  more general one, such as obj.attr.subattr."""

  # Notice that dotted.names in comments and literal strings are ignored

  from oopp import *
  import __main__

  text=inspect.getsource(__main__)

  regexp=Regexp.CODESEP| Regexp.DOTNAME()

  print 'Using the regular expression',regexp

  print "I have found the following dotted names:\n%s" % [
      MO.group() for MO in regexp.finditer(text) if MO.lastgroup=='DOTNAME']
  
  #</test_re.py>

with output:

 ::

  Using the regular expression (?s)(?P<COMMENT>#.*?(?=\n))|(?P<STRING>
  '''.+?'''|""".+?"""|'.+?'|".+?")|(?P<DOTNAME>[a-zA-Z_]\w*\.[\w\.]*)
  I have found the following dotted names:
  ['inspect.getsource', 'Regexp.CODESEP', 'Regexp.DOTNAME.__call__', 'MO.group', 
   'dotname.finditer', 'MO.lastgroup']

Now one can define a good ``CodeStr`` class with replacing features

Let me consider for instance the solution to the problem discussed in chapter
4, i.e. the definition of a ``TextStr`` class able to indent and dedent
blocks of text.

 ::

  #<oopp.py>
 
  def codeprocess(code,TPO): # TPO=text processing operator
      code=code.replace("\\'","\x01").replace('\\"','\x02')
      genblock,out = Regexp.CODESEP.generateblocks(code),[]
      for block in genblock:
          out.append(TPO(block))
          out.append(genblock.next())
      return ''.join(out).replace("\x01","\\'").replace('\x02','\\"')

  def quotencode(text):
      return text.replace("\\'","\x01").replace('\\"','\x02')

  def quotdecode(text):
      return text.replace("\x01","\\'").replace('\x02','\\"')

  #</oopp.py>

Here is an example of usage: replacing 'Print' with 'print' except in
comments and literal strings.

 ::

  #<codeproc.py>

  from oopp import codeprocess

  wrongcode=r'''
  """Code processing example: replaces 'Print' with 'print' except in
  comments and literal strings"""
  Print "This program prints \"Hello World!\"" # look at this line!
  '''

  fixPrint=lambda s: s.replace('Print','print')
  validcode=codeprocess(wrongcode,fixPrint)

  print 'Source code:\n',validcode
  print 'Output:\n'; exec validcode
  
  #</codeproc.py>
 
with output

 ::

  Source code:

  """Code processing example: replaces 'Print' with 'print' except in
  comments and literal strings"""
  print "Prints \"Hello World!\"" # look at this line!

  Output:

  This program prints "Hello World!"

A simple state machine
---------------------------------------------------------------------------

Regular expression, however powerful, are limited in scope since they
cannot recognize recursive structures. For instance, they cannot parse
parenthesized expression.

The simplest way to parse a parenthesized expression is to use a state
machine.

 ::

  (?:...) non-grouping
  (?P<name>...) 

  (?=...) look-ahead 
  (?!...) negative
  (?<=...) look-behind 
  (?<!...) negative

  dec=\
  """
  paren   = ( .. )
  bracket = [ .. ]
  brace   = { .. }
  comment = # .. \n
  """

  actions=\
  """
  reobj1  : R' .. (?<!\\)' -> Regexp(r''' .. ''')
  reobj2  : R" .. (?<!\\)" -> Regexp(r""" .. """)
  string1 : (?<!')'(?!') .. (?<!\\)'(?!') -> ''' .. '''
  string2 : (?<!")"(?!") .. (?<!\\)"(?!") -> """ .. """
  """

  beg=0; end=1

  string1[beg]=r"(?<!')'(?!')" # an isolated single quote
  string2[beg]=r'(?<!")"(?!")' # an isolated double quote
  string1[end]=r"(?<!\\)'(?!')" # ending single quote
  string2[end]=r'(?<!\\)"(?!")' # ending double quote

  reobj1[beg]=r"R'"       
  reobj2[beg]=r'R"' 
  reobj1[end]=string1[end] # ending single quote
  reobj2[end]=string2[end] # ending double quote

  actions=\
  """
  reobj1  : R' .. (?<!\\)' -> Regexp(r''' .. ''')
  reobj2  : R" .. (?<!\\)" -> Regexp(r""" .. """)
  string1 : (?<!')'(?!') .. (?<!\\)'(?!') -> ''' .. '''
  string2 : (?<!")"(?!") .. (?<!\\)"(?!") -> """ .. """
  """

  beg={}; end={}; ls=[]
  for line in decl.splitlines():
     mode,rest=line.split(' : ')
     s,r=rest.split(' -> ')

   beg[mode],end[mode]=s.split(' .. ')
   ls.append('(?P<beg_%s>%s)' % (mode,beg[mode]))
   ls.append('(?P<end_%s>%s)' % (mode,end[mode]))

   beg2[mode],end2[mode]=r.split(' .. ')
   ls.append(beg2[mode])
   ls.append(end2[mode])

  delimiters='(%s)' % re.compile('|'.join(ls))
  splitlist=['']+delimiters.split(source)
  for delim,text in splitlist:
      delimiters.match(delim).lastgroup

Creating classes
----------------------------------------------------------------------

TODO

Modifying modules
-----------------------------------------------------------

Metaclasses are extremely
useful since they allows to change the behaviour of the code without
changing the sources. For instance, suppose you have a large library written 
by others that you want to enhance in some way.

Typically, it is always a bad idea to modify the sources, for many reasons:

+ touching code written by others, you may introduce new bugs;
+ you may have many scripts that requires the original version 
  of the library, not the modified one;
+ if you change the sources and then you buy the new version of the
  library, you have to change the sources again!

The solution is to enhance the proprierties of the library at run
time, when the module is imported, by using metaclasses.

To show a concrete example, let me consider the case of the module
*commands* in the Standard Library. This module is Unix-specific,
and cannot be used under Windows. It would be nice to have a
metaclass able to enhance the module in such a way that
when it is invoked on a Windows platform, Windows specific replacement
of the Unix functions provided in the module are used. However,
for sake of brevity, I will only give a metaclasses that display
a nice message in the case we are in a Window platform, without
raising an error (one could easily implement such a behaviour,
however).

 ::

  #<recognizewindows.py>

  import oopp,sys,commands

  class WindowsAware(type):
      def __init__(cls,*args): 
          if sys.platform=='win32': 
              for key,val in vars(cls).iteritems():
                  if isinstance(val,staticmethod):
                      setattr(cls,key,staticmethod(lambda *args: 
                           "Sorry, you are (or I pretend you are) on Windows,"
                           " you cannot use the %s.module" % cls.__name__))
            
  sys.platform="win32" #just in case you are not on Windows

  commands=oopp.ClsFactory[WindowsAware](commands)

  print commands.getoutput('date') #cannot be executed on Windows

  #</recognizewindows.py>

The output of this script is 

 ::

  Sorry, you are on Windows, you cannot use the commands.module

However, if you are on Linux and you comment out the line 

 ::

  sys.platform="win32" 

you will see that the script works.

Notice that the line ``commands=WindowsAware(commands)`` actually
converts the 'commands' module in a 'commands' class, but since
the usage is the same, this will fool all programs using the
commands module. In this case the class factory 'WindowsAware'
can also be thought as a module modifier. In this sense, it is
very useful to denote the metaclass with an *adjective*.

Metaclasses and attribute descriptors 
----------------------------------------------------------------------

Descriptors are especially useful in conjunction with metaclasses, since
a custom metaclass can use them as low level tools to modify the methods 
and the attributes of its instances. This allows to implement very 
sophisticated features with few lines of code.


Notice, anyway, that
even plain old function can be thought of as of descriptors. 

Descriptors share at least two features with metaclasses:

1. as metaclasses, descriptors are best used as adjectives, since they
   are intended to modify and enhance standard methods and attributes, in the
   same sense metaclasses modify and enhance standard classes;

2. as metaclasses, descriptors can change the *semantics* of Python, i.e.
   what you see is not necessarely what you get. As such, they are a 
   dangerous feature. Use them with judgement!

Now I will show a possible application of properties.

#<readonly.py>

  "Making read-only attributes"

  class Readonly(type):
      def __init__(cls,name,bases,dic):
          super(Readonly,cls).__init__(name,bases,dic) # cooperative __init__
          readonly=dic.get('__readonly__',[])
          for attr in readonly:
              get=lambda self,value=getattr(cls,attr): value
              setattr(cls,attr,property(get))
            
  class C:
      __metaclass__=Readonly
      __readonly__='x','y'
      x=1
      y=2

  c=C()

  print c.x,c.y
  try:
      c.x=3 
  except Exception,e:
      print e

  #</readonly.py>

Suppose one has a given class with various kind of
attributes (plain methods, regular methods, static methods,
class methods, properties and data attributes) and she wants
to trace to access to the data attributes (notice that the motivation
for the following problem come from a real question asked in
comp.lang.python). Then one needs to retrieve data
attributes from the class and convert them in properties
controlling their access syntax. The first problem is solved
by a simple function

 ::

  #<oopp.py>

  def isplaindata(a):
      """A data attribute has no __get__ or __set__ attributes, is not
      a built-in function, nor a built-in method.""" 
      return not(hasattr(a,'__get__') or hasattr(a,'__set__')
                 or isinstance(a,BuiltinMethodType) or
                 isinstance(a,BuiltinFunctionType))

  #</oopp.py>

whereas the second problem is elegantly solved by a custom metaclass:

 ::

  #<tracedaccess.py>

  from oopp import isplaindata,inspect

  class TracedAccess(type):
      "Metaclass converting data attributes to properties"
      def __init__(cls,name,bases,dic):
          cls.datadic={}
          for a in dic:
              if isplaindata(a):
                  cls.datadic[a]=dic[a]
                  def get(self,a=a):
                      v=cls.datadic[a]
                      print "Accessing %s, value=%s" % (a,v)
                      return v
                  def set(self,v,a=a):
                      print "Setting %s, value=%s" % (a,v)
                      cls.datadic[a]=v
                  setattr(cls,a,property(get,set))

  class C(object):
      __metaclass__ = TracedAccess
      a1='x'

  class D(C): # shows that the approach works well with inheritance
      a2='y'

  i=D()
  i.a1 # => Accessing a1, value=x
  i.a2 # => Accessing a2, value=y
  i.a1='z' # => Setting a1, value=z
  i.a1 # => Accessing a1, value=z

  #</tracedaccess.py>

In this example the metaclass looks at the plain data attributes (recognized
thanks ot the ``isplaindata`` function) of its instances and put them
in the dictionary ``cls.datadic``. Then the original attributes are replaced
with property objects tracing the access to them. The solution is a 4-line 
custom metaclass doing the boring job for me:

 ::

  #<oopp.py>
  
  class Wrapped(Customizable,type):
      """A customizable metaclass to wrap methods with a given wrapper and
      a given condition"""
      __metaclass__=Reflective
      wrapper=wrappedmethod
      condition=lambda k,v: True # wrap all
      def __init__(cls,*args):
          super(cls.__this,cls).__init__(*args)
          wrap(cls,cls.wrapper,cls.condition.im_func)

  Traced=Wrapped.With(wrapper=tracedmethod,__name__='Traced')
  Timed=Wrapped.With(wrapper=timedmethod,__name__='Timed')

  #</oopp.py>

Here is an example of usage:

  >>> from oopp import *
  >>> time_=ClsFactory[Traced](time)
  >>> print time_.asctime()
  [time_] Calling 'asctime' with arguments
  (){} ...
  -> 'time_.asctime' called with result: Sun May  4 07:30:51 2003
  Sun May  4 07:30:51 2003

Another is

 ::

  #<tracemain.py>

  from oopp import ClsFactory,Traced,Reflective

  def f1(x): return x     # nested functions 
  def f2(x): return f1(x) # we want to trace

  f1orf2=lambda k,v : v is f1 or v is f2
  make=ClsFactory[Reflective,Traced.With(condition=f1orf2)]
  traced=make('traced',globals())

  traced.f2('hello!') # call traced.f2

  #</tracemain.py>

with output

 ::

  [__main__] Calling 'f2' with arguments
  ('hello!',){} ...
  [__main__] Calling 'f1' with arguments
  ('hello!',){} ...
  -> '__main__.f1' called with result: hello!
  -> '__main__.f2' called with result: hello!

Modifying hierarchies
---------------------------------------------------------

Suppose one wants to enhance a pre-existing class, for instance
by adding tracing capabilities to it. The problem is non-trivial
since it is not enough to derive a new class from the original
class using the 'Traced' metaclass. For instance, we could imagine of 
tracing the 'Pizza' class introduced in chapter 4 by defining

  >>> from oopp import *
  >>> class TracedTomatoPizza(GenericPizza,WithLogger):
  ...     __metaclass__=ClsFactory[Traced] 
  ...     toppinglist=['tomato']

However, this would only trace the methods of the newly defined class,
not of the original one. Since the new class does not introduce any 
non-trivial method, the addition of 'Traced' is practically without
any effect:

  >>> marinara=TracedTomatoPizza('small') # nothing happens
  *****************************************************************************
  Tue Apr 15 11:00:17 2003
  1. Created small pizza with tomato, cost $ 1.5

Tracing hierarchies
------------------------------------------------------------------------------

 ::

  #<traceH.py>

  from oopp import *

  def wrapMRO(cls,wrapped):
      for c in cls.__mro__[:-1]:
          wrap(c,wrapped)

  tracing=tracedmethod.With(logfile=file('trace.txt','w'))
  wrapMRO(HomoSapiensSapiens,tracing)
  HomoSapiensSapiens().can()
  
  #</traceH.py>

with output in trace.txt

 ::

  [HomoSapiensSapiens] Calling 'can' with arguments
   (<oopp.HomoSapiensSapiens object at 0x4020364c>,){} ...
      [HomoSapiens] Calling 'can' with arguments
       (<oopp.HomoSapiensSapiens object at 0x4020364c>,){} ...
          [HomoHabilis] Calling 'can' with arguments
           (<oopp.HomoSapiensSapiens object at 0x4020364c>,){} ...
              [Homo] Calling 'can' with arguments
               (<oopp.HomoSapiensSapiens object at 0x4020364c>,){} ...
                  [PrettyPrinted] Calling '__str__' with arguments
                   (<oopp.HomoSapiensSapiens object at 0x4020364c>,){} ...
                  [PrettyPrinted.__str__] called with result: 
                   <HomoSapiensSapiens>
              [Homo.can] called with result: None
          [HomoHabilis.can] called with result: None
      [HomoSapiens.can] called with result: None
  [HomoSapiensSapiens.can] called with result: None

Modifying source code
------------------------------------------------------------------------

The real solution would be to derive the original class 'GenericPizza'
from 'Traced' and not from 'object'. One could imagine of creating
a new class inhering from 'Traced' and with all the methods of the
original 'GenericPizza' class; then one should create copies of
all the classes in the whole multiple inheritance hierarchy.
This would be a little annoying, but feasable; the real problem is
that this approach would not work with cooperative methods, since 
cooperative calls in the derived classes would invoked methods in 
the original classes, which are not traced. 

This is a case where the modification of the original source code is 
much more appealing and simpler that any other method: it is enough 
to perform a search and replace in the original source code, by adding
the metaclass 'Traced', to enhance the whole multiple inheritance hierarchy.
Let me assume that the hierarchy is contained in a module (which is
typical case). The idea, is to generate *dynamically* a new module from the
modified source code, with a suitable name to avoid conflicts with the
original module. Incredibily enough, this can be done in few lines:

 ::

  #<oopp.py>

  def modulesub(s,r,module):
      "Requires 2.3"
      name=module.__name__
      source=inspect.getsource(module).replace(s,r)
      dic={name: module}; exec source in dic # exec the modified module
      module2=ModuleType(name+'2') # creates an an empty module 
      customize(module2,**dic) # populates it with dic
      return module2
    
  #</oopp.py>

Notice that the ``sub`` function, that modifies the source code of
a given module and returns a modified module, requires Python 2.3
to work. This is a due to a subtle bug in ``exec`` in Python 2.2.
Anyway, the restriction to Python 2.3 allows me to take advantage
of one of the most elegant convenience of Python 2.3: the name in
the ``types`` module acts are type factories and in particular
``ModuleType(s)`` returns an (empty) module named ``s``.
Here is an example of usage:
 
  >>> import oopp
  >>> s='GenericPizza(object):'
  >>> oopp2=oopp.modulesub(s,s+'\n    __metaclass__=oopp.Traced',oopp) 

Name clashes are avoided, being 'oopp2' a different module from 
'oopp'; we have simultaneously access to both the original hierarchy
in 'oopp' (non-traced) and the modified one in 'oopp2' (traced).
In particular 'oopp2.CustomizablePizza' is traced and therefore

  >>> class PizzaLog(oopp2.CustomizablePizza,oopp2.WithLogger):
  ...     __metaclass__=makecls()
  >>> marinara=PizzaLog.With(toppinglist=['tomato'])('small')

gives the output

 ::

  [PizzaLog] Calling '__init__' with arguments
  (<oopp.PizzaLog object at 0x40470dac>, 'small'){} ...
  -> 'PizzaLog.__init__' called with result: None

  *****************************************************************************
  Thu Mar 27 09:18:28 2003
  [PizzaLog] Calling '__str__' with arguments
  (<oopp.PizzaLog object at 0x40470dac>,){} ...
  [PizzaLog] Calling 'price' with arguments
  (<oopp.PizzaLog object at 0x40470dac>,){} ...
  [PizzaLog] Calling 'toppings_price' with arguments
  (<oopp.PizzaLog object at 0x40470dac>,){} ...
  -> 'PizzaLog.toppings_price' called with result: 0.5

  -> 'PizzaLog.price' called with result: 1.5

  -> 'PizzaLog.__str__' called with result: small pizza with tomato, cost $ 1.5

  1. Created small pizza with tomato, cost $ 1.5

From that we understand what is happening:

- ``PizzaLog.__init__`` calls ``GenericPizza.__init__`` that defines size and
  cooperatively calls ``WithLogger.__init__`` 

- WithLogger.__init__ cooperatively calls ``WithCounter.__init__`` 
  that increments the count attribute;

- at this point, the instruction 'print self' in ``WithLogger.__init__`` calls 
  ``PizzaLog.__str__`` (inherited from ``GenericPizza.__str__``);

- ``GenericPizza.__str__`` calls 'price' that in turns calls 
  'toppings_price'.

On top of that, notice that the metaclass of 'PizzaLog' is
``_TracedReflective`` that has been automagically generated by 
``makecls`` from the metaclasses of 'CustomizablePizza' (i.e. 'Traced')
and of 'WithLogger' (i.e. 'Reflective'); the leading underscore helps 
to understand the dynamical origin of '_TracedReflective'.
It turns out that '_TracedReflective' has a dynamically
generated (meta-meta)class:

  >>> print type(type(PizzaLog)) #meta-metaclass
  <class 'oopp._WithWrappingCapabilitiesReflective'>

Therefore this example has a non-trivial class hierarchy

  >>> print oopp.MRO(PizzaLog)
  MRO of PizzaLog:
    0 - PizzaLog(CustomizablePizza,WithLogger)[Traced]
    1 - CustomizablePizza(GenericPizza,Customizable)[Traced]
    2 - GenericPizza(object)[Traced]
    3 - WithLogger(WithCounter,Customizable,PrettyPrinted)
    4 - WithCounter(object)
    5 - Customizable(object)
    6 - PrettyPrinted(object)
    7 - object()

a non-trivial metaclass hierarchy,

  >>> print oopp.MRO(type(PizzaLog)) # the metaclass hierarchy
  MRO of Traced:
    0 - Traced(Reflective)[WithWrappingCapabilities]
    1 - Reflective(type)
    2 - type(object)
    3 - object()

and a non-trivial meta-metaclass hierarchy:

  >>> print oopp.MRO(type(type(PizzaLog))) # the meta-metaclass hierarchy
  MRO of WithWrappingCapabilities:
    0 - WithWrappingCapabilities(BracketCallable)
    1 - CallableWithBrackets(type)
    2 - type(object)
    3 - object()

Pretty much complicated, isn't it ? ;)

This example is there to show what kind of maintenance one can have
with programs doing a large use of metaclasses, particularly, when
they should be understood by somebody else than the autor ...

Metaclass regenerated hierarchies
--------------------------------------------------------------------------

 ::

  import types
    
  def hierarchy(self,cls):
      d=dict([(t.__name__,t) for t in vars(types).itervalues()
              if isinstance(t,type)])
      def new(c):
          bases=tuple([d[b.__name__] for b in c.__bases__])
          return self(c.__name__, bases, c.__dict__.copy())
      mro=list(cls.__mro__[:-1])
      mro.reverse()
      for c in mro:
          if not c.__name__ in d:
              d[c.__name__]=new(c)
      customize(self,**d)

  ClsFactory.hierarchy=hierarchy
  traced=ClsFactory[Traced,Reflective]

Unfortunately, this approach does not work if the original hierarchy makes
named cooperative super calls.

Therefore the source-code run-time modification has its advantages.