summaryrefslogtreecommitdiff
path: root/mtraits/doc.py
blob: fe231f58293dfed7790ebffec50f9eff586e4415 (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
r"""An implementation of traits in Python
==================================================================

:Author: Michele Simionato
:Date: XXX
:Version: XXX
:Download: XXX
:Licence: BSD
:Status: XXX

Motivation
------------------------------------------------

Multiple inheritance is a hotly debated topic. 
The supporters of multiple inheritance 
claim that it makes code shorter and easier
to read, whereas the opposers claim that is makes
code more coupled and more difficult to understand.  I have
spent some time in the past facing the intricacies of `multiple
inheritance in Python`_ and I was one of its supporters once; however,
since then I have worked with frameworks making
large use of multiple inheritance (I mean Zope 2) and nowadays I am in
the number of the people who oppose it.  Therefore I
am interested in alternatives.

In recent years, the approach
of traits_ has gained some traction in a few circles and I have
decided to write a library to implement traits in Python, for
experimentation purposes. The library is meant for framework builders,
people who are thinking about writing a framework based on multiple
inheritance - typically via the common mixin approach - but
are not convinced that this is the best solution and would like to try
an alternative. This library is also for authors of mixin-bases frameworks
which are unsatisfied and would like to convert their
framework to traits.

Are traits a better solution than multiple inheritance and mixins?  In
theory I think so, otherwise I would not have written this library, but
in practice (as always) things may be different. It may well be 
in practice that using traits or using mixins does not make a big
difference and that the change of paradigm is not worth the effort; or the
opposite may be true. The only way to know is to try, to build
software based on traits and to see how it scale *in the large*.
In the small, of course, more or less any approach works fine: it is only
by programming in the large that you can see the differences.  

This is
the reason why I am releasing this library with a liberal licence, so
that people can try it out and see how it works. The library is meant
to play well (when possible) with pre-existing frameworks. 
As an example, I will show
here how you could rewrite Tkinter classes to use traits instead of mixins. Of
course, I am not advocating rewriting Tkinter: it would be silly 
and pointless; but it may have sense (or
not) to rewrite your own framework using traits, perhaps a framework
which is used in house but has not been released yet.

I am not the only one to have
implemented traits for Python; after finishing my implementation
I made a little research and discovered a few implementations. The
biggest effort seems to be `Enthought Traits`_ which however is
large implementation and seems to use the name to intend something very
different (i.e. a sort of type checking). My implementation has no
dependencies, is short and I am committed
to keep it short even in the future, according to
the principle of `less is more`_. 

There is also an hidden agenda behind this module: to popularize some
advanced features of the Python object model which are little
known. The ``mtrait`` module is actually a tribute to the
metaprogramming capabilities of Python: such features are usually
associated to languages with a strong academic tradition - Smalltalk,
Scheme, Lisp - but actually the Python object model is no less
powerful.  For instance, changing the object system from a multiple
inheritance one to a trait-based one, with *different* lookup rules,
can be done *within* the fundamental object system. The reason is that
the features that Guido used to implement the object system (special
method hooks, descriptors, metaclasses) are there, available to the
end user to build her own object system.

Such features are usually little used in the Python community for
various good reasons: the object system is good enough as it is and there
is no reason to change it; moreover there is a strong opposition to
change the language, because Python programmers believe in uniformity
and in using common idioms; finally, it is difficult for application
programmer to find a domain where these features are useful. An
exception is the domain of the Object Relation Mappers, whereas the
Python language is often stretched to mimic the SQL language, a famous
example of this tendency being SQLAlchemy_).
Still, I have never seen a perversion of the object model as big
as the one implemented in the ``mtrait`` module, so I wanted to
be the first one to perform that kind of abuse ;) 

.. _multiple inheritance in Python: MRO
.. _less is more: http://www.artima.com/weblogs/viewpost.jsp?thread=236286
.. _Enthought Traits: https://svn.enthought.com/enthought/wiki/Traits

What are traits?
------------------------------------------------------------

The word *traits* has many meanings; I will refer to it in the sense
of the paper `Traits - Composable Units of Behavior`_ which implements
them in Squeak/Smalltalk. The paper appeared in 2003, but most of the
ideas underlying traits have been floating around for at least 30
years. There is also a trait implementation for `PLT Scheme`_ which is
somewhat close in spirit (if not in practice) to what I am advocating here.
The library you are reading about is by no means intended as a porting
of the Smalltalk library: I am just stealing some of the ideas from
that paper to implement a Pythonic alternative to mixins which, for
lack of a better name, I have decided to call traits. I feel no
obligation whatsoever to be consistent with the Smalltalk library. In
doing so, I am following a long tradition, since a lot of languages
use the name *traits* to mean something completely different from the
Smalltalk meaning. For instance the languages Fortress and Scala use
the name *trait* but they mean by it what is usually called a *mixin*.
For me a trait is a bunch of methods and attributes with the following
properties:

1. the methods/attributes in a trait go logically together;
2. if a trait enhances a class, then all subclasses are enhanced too;
3. if a trait has methods in common with the class, then the
   methods defined in the class have the precedence;
4. the ordering of traits is not important, i.e. enhancing a class 
   first with trait T1 and then with trait T2 or viceversa is the same;
5. if traits T1 and T2 have names in common, enhancing a class both
   with T1 and T2 raises an error unless unless you specify *explicitely*
   how the overriding of the common names has to be made;
6. if a trait has methods in common with the base class, then the
   trait methods have the precedence;
7. a class can be seen both as a composition of traits and as an homogeneous
   entity.

Properties from 4 to 7 are the distinguishing properties of traits
with respect to multiple inheritance and mixins.  In particular,
because of 4 and 5, all the complications with the Method Resolution
Order disappear and the overriding is never implicit.  Property 6 is
mostly unusual: typically in Python the base class has the precedence
over mixin classes.  Property 7 has to be intended in the sense that a
trait implementation must provide introspection facilities to make
seemless the transition between classes viewed as atomic entities and
as composed entities.

A hands-on example
------------------------------------------------------

Let me begin by showing how you could rewrite a
Tkinter class to use traits instead of mixins. Consider the
``Tkinter.Widget`` class, which is derived by the base class
``BaseWidget`` and the mixin classes
``Tkinter.Grid``, ``Tkinter.Pack`` and ``Tkinter.Place``: we want to
rewrite it to use traits. The ``mtrait`` module
provides a class decorator named ``include`` to do the job; it is
enough to replace the multiple inheritance syntax

.. code-block:: python

  class Widget(BaseWidget, Grid, Pack, Place):
       pass

with a class decorator syntax:

.. code-block:: python

 @include(Pack, Place, Grid) # this syntax requires Python 2.6+
 class Widget(BaseWidget):
     pass

For compatibility with old versions of Python (before Python 2.6)
the ``include`` class decorator provides some magic to work even
inside classes:

.. code-block:: python

 class Widget(BaseWidget): # this syntax works is backward-compatible
     include(Pack, Place, Grid)

The preferred way however is to use the ``@`` notation, if available.
I said that the conversion from mixins to traits was easy: but actually
I lied since if you try to execute the code I just wrote you will
get an ``OverridingError``:

.. code-block:: python

 >>> from Tkinter import *
 >>> class Widget(BaseWidget):
 ...     include(Pack, Place, Grid)
 Traceback (most recent call last):
   ...
 OverridingError: Pack overrides names in Place: {info, config, configure, slaves, forget}

The reason for the error is clear: both ``Pack`` and ``Place`` provide
methods called ``{info, config, configure, slaves, forget}`` 
and the traits implementation cannot figure out
which ones to use.  This is a feature, since it forces you to be
explicit. In this case, if we want to be consistent with
multiple inheritance rules, clearly we want the methods coming from
the first class (i.e. ``Pack``) to have precedence. That can be
implemented by including directly those methods in the class namespace
and relying on rule 3:

$$TOSWidget

Notice that we had to specify the ``propagate`` method too, since
it is common between ``Pack`` and ``Grid``.

You can check that the TOSWidget class works, for instance by defining a
label widget as follows (remember that ``TOSWidget`` inherits its signature
from  ``BaseWidget``):

.. code-block:: python

 >>> label = TOSWidget(master=None, widgetName='label',
 ...                   cnf=dict(text="hello"))

You may visualize the widget by calling the ``.pack`` method:

.. code-block:: python

 >>> label.pack()

This should open a small window with the message "hello" inside it.

A few caveats
----------------------------------------------------

As you see, in a lot of cases replacing mixins with traits is fairly
straightforward: however, in some cases you can run into problems.
For instance, rule 6 implies an attribute lookup different from
the usual lookup. Consider the following example:

.. code-block:: python

 >>> class Base(object):
 ...     a = 1

 >>> class ATrait(object):
 ...     a = 2

 >>> class Class(Base):
 ...     include(ATrait)

 >>> Class.a
 2

In regular multiple inheritance instead the base class attribute
would have the precedence:

.. code-block:: python

 >>> type('Class', (Base, ATrait), {}).a
 1

Therefore replacing mixin classes with traits can break your code if
you rely on the standard overriding rules. Be careful!

Also, you should be aware of the fact that *special methods
are special*: the ``mtrait`` module perverts the Python object system
to follow rules 1-7 for all attribute access *except* for special
attributes. Special attributes of the form ``__xxx___`` in a trait
are just *ignored*:

.. code-block:: python

 >>> ATrait.__special__ = 3 # this attribute is NOT transmitted to Class
 >>> hasattr(Class, '__special__')
 False

Finally, if you have a cooperative multiple inheritance hierarchy
(i.e. you use ``super`` in the mixin classes) you will have to rewrite
your code not to use ``super`` (notice however that there are no
problem if you use ``super`` ony in methods of the base class). This
is on purpose: traits are not a replacement for cooperative multiple
inheritance, you should uses traits if you think that cooperative
multiple inheritance is a bad idea and you are willing to remove it
from your code. Old frameworks written before the introduction of
``super`` (such as Tkinter and Zope 2) are easier to convert in this
respect, since they are non-cooperative.  The ``mtrait`` module is
intended for framework writers, so it assumes you can change the
source code of your framework if you want; of course, it aims to
reduce the needed changes as much as possible.

How does it work
---------------------------------------------------------

The implementation of traits provided is an ``mtrait`` is short (under
300 lines of code including comments and docstrings) but if makes use
of fairly sophisticated techniques. The building block is a
``Trait`` class which you can use directly, instead of relying on the
magic of ``include``. You can see a trait as a wrapper around an inner
object.  Usually you want to convert a mixin class into a trait; you
can do it as follows:

  ``trait = Trait(Mixin, Mixin.__name__)``

It is also common to convert a module into a trait:

  ``trait = Trait(module, module.__name__)``

Trait objects are attribute descriptors, i.e. they can used as class
attributes; when a trait is called from a class or an instance, we
say that it is bound to the class or the instance.

Here is an example of usage:

.. code-block:: python

 >>> pack = Trait(Pack, 'Pack') # define an unbound trait
 >>> pack
 <Trait Pack {config, configure, forget, info, pack, pack_configure, pack_forget, pack_info, pack_propagate, pack_slaves, propagate, slaves} >

 >>> class MyWidget(BaseWidget): # add the trait to a class
 ...      p = pack

Here is an example of trait a bound to a class:

.. code-block:: python

 >>> MyWidget.p #doctest: +ELLIPSIS
 <Trait Pack {config, configure, forget, info, pack, pack_configure, pack_forget, pack_info, pack_propagate, pack_slaves, propagate, slaves} bound to <class __main__.MyWidget at 0x...>>

Here is an example of a trait bound to an instance:

.. code-block:: python

 >>> lbl = MyWidget(None, 'label', dict(text='hello'))
 >>> lbl.p #doctest: +ELLIPSIS
 <Trait Pack {config, configure, forget, info, pack, pack_configure, pack_forget, pack_info, pack_propagate, pack_slaves, propagate, slaves} bound to <__main__.MyWidget instance at 0x...>>

You can call the methods of a trait as follows:

.. code-block:: python

 >>> lbl.p.pack()
 >>> lbl.p.info().keys()
 ['side', 'ipady', 'ipadx', 'in', 'pady', 'padx', 'anchor', 'expand', 'fill']

The descriptor magic works in such a way that the instance methods get
as first argument the instance to which the trait is bound; on the other
hand, for traits bound to classes, you get an unbound method associated
to the given class:

.. code-block:: python

 >>> MyWidget.p.pack
 <unbound method MyWidget.pack_configure>

Different traits or mixins can be composed into a ``TraitContainer``:
for instance, you could define

.. code-block:: python

 >>> tc = TraitContainer.from_([Pack, Place, Grid])
 >>> tc
 <Traits Grid, Place, Pack >

The ``TraitContainer`` object has attributes ``Pack``, ``Place`` and ``Grid``
which are traits corresponding to the mixin classes; for instance

.. code-block:: python

 >>> tc.Pack
 <Trait Pack {config, configure, forget, info, pack, pack_configure, pack_forget, pack_info, pack_propagate, pack_slaves, propagate, slaves} >

Internally the ``include`` decorator builds a trait object for each mixin and
store all of them into the ``__traits__`` attribute descriptor,
making your traits ready for introspection (rule 7):

.. code-block:: python

 >>> TOSWidget.__traits__ # bound-to-class TraitContainer
 <Traits Grid, Place, Pack bound to <class '__main__.TOSWidget'>>

 >>> label.__traits__ #doctest: +ELLIPSIS
 <Traits Grid, Place, Pack bound to <__main__.TOSWidget object at 0x...>>

Traits play well with pydoc, ipython and the other tools used
to introspect Python objects: try to type
``help(lbl)`` and you will see for yourself how it works.

Beyond the looking glass
----------------------------------------------------------------------

The goal of the ``mtrait`` module it to modify the standard
Python object model, turning it into a Trait Object System (TOS for short): 
TOS classes behave differently from regular
classes. In particular TOS classes must not support multiple inheritance
and must forbid your from defining your own ``__getattribute__`` and 
``__getstate__`` methods; these
two fundamental properties must be preserved under inheritance (i.e. the
son of a TOS class must be a TOS class) and therefore the implementation
requires necessarily metaclasses.

In theory you could build your trait objects yourself as explained in
the previous paragraph, and you could implement the dispatch to traits
by hand; in practice however it is much easier if you just rely on the magic of
the ``include`` class decorator, which is doing a lot of work on your
behalf. In particular, internally the class decorator works its magic
by changing the metaclass of the original class to a subclass of
``MetaTOS``, the basic class of the Trait Object System. ``MetaTOS``
does a lot of work: in particular it adds a suitable ``__getattribute__``
method to its instances. 

We need to override the
``__getattribute__`` method 
since we want to change the attribute lookup rules: in regular
Python, the usual rules are 1. look at the class 2. look at
the base class and 3. look at ``__getattr__``; rules 6 instead
says that traits must have the precedence over the base class,
so overriding ``__getattr__`` would not be enough. Fortunately,
the Python object model is powerful enough to allows users
to change the rules of the game: by overriding ``__getattribute__``
it is possible to lookup at the traits attributes *before* looking
at the base class. Notice that is necessary to override
``__getattribute__`` both at the class level and at the metaclass
level, to be able to manage both instance attributes and class
attributes.

``MetaTOS`` also adds a suitable ``__getstate_``
method so that your objects stay pickleable if they were originally
pickleable (adding ``__getattribute__`` without adding ``__getstate__`` would
break pickle). Notice that the original class should not define
``__getattribute__`` or ``__getstate__`` of its own, otherwise an 
``OverridingError`` is raised. This is intended to prevent
accidental overriding; if you really know what you are doing,
you can always replace the default implementation with your own
after class creation.

People wanting to do everything explicitely can use
the ``__metaclass__`` hook and specify the mixins directly with ``__mixins__``:

$$TOSWidget2

However, ``include`` does more than that, since it
takes care automatically of possible metaclass conflicts. In the case of
Tkinter there is no such problem, since ``BaseWidget`` is just a traditional
old-style class and the metaclass for ``TOSWidget2`` is just ``TOSMeta``:

.. code-block:: python

 >>> type(TOSWidget)
 <class 'mtrait.TOSMeta'>

Nevertheless, in general you may need to build your Trait Based Framework
on top of pre-existing classes with a nontrivial metaclass, for
instance Zope classes; in that case having a class decorator smart
enough to figure out the right metaclass to use is a convenient
facility. Here is an example:

$$MyMetacls

.. code-block:: python

 >>> class Base:
 ...     __metaclass__ = MyMetacls
 ...     include(Pack)
 ...
 >>> print type(Base)
 <class 'noconflict._TOSMetaMyMetacls'>

The ``include`` decorator automatically generates the right metaclass
which avoids the dreaded `metaclass conflict`_, a daughter of
``TOSMeta`` and ``MyMetacls``:

.. code-block:: python

 >>> print type(Base).__bases__
 (<class 'mtrait.TOSMeta'>, <class '__main__.MyMetacls'>)

The name is automatically generated from the name of the base
metaclasses; moreover, a register of the generated metaclasses
is kept, so that metaclasses are reused if possible.
If you want to understand the details, you are welcome
to give a look at the implementation, which is pretty small.

.. _sqlalchemy: http://www.sqlalchemy.org/
.. _metaclass conflict: http://code.activestate.com/recipes/204197/

Why multiple inheritance is forbidden
----------------------------------------------------------

As I said, the mother metaclass of the Trait Object System ``MetaTOS`` forbids
multiple inheritance and if you try to multiple inherit from a TOS
class and another class you will get a ``TypeError``:

.. code-block:: python

 >>> class M: pass
 ... 
 >>> class Widget2(TOSWidget, M): #doctest: +ELLIPSIS
 ...     pass
 ...
 Traceback (most recent call last):
   ...
 TypeError: Multiple inheritance of bases (<class '__main__.TOSWidget'>, <class __main__.M at 0x...>) is forbidden for TOS classes

This behavior is intentional: with this restriction you can simulate
an ideal world in which Python did not support multiple
inheritance. Suppose you want to claim that supporting multiple
inheritance was a mistake and that Python would have been better
without it (which is the position I tend to have nowadays, but see the
notes below): how can you prove that claim? Simply by writing code
that does not use multiple inheritance and it is clearer and more
mantainable that code using multiple inheritance. 

I am releasing this
trait implementation hoping you will help me to prove (or possibly
disprove) the point.  You may see traits as a restricted form of
multiple inheritance without method resolution order and without name
clashes which does not pollute the namespace of the original class; it
does not have cooperative methods either. Still I think these are
acceptable restrictions since they give back in return many advantages
in terms of simplicity: for instance, ``super`` becomes trivial, since
each class has a single superclass.  

We all know that the `current
super in Python`_ is very far from trivial, instead. More importantly,
we all know that. many people use multiple inheritance incorrectly,
confusing the ``is-a`` relation with the ``has-a`` relation; with
traits, there is no confusion. Since there is a single base class, you
can associate the ``is-a`` relation with the base class whereas the
features coming from the traits correspond to ``has-a``: for instance
in the Tkinter example a ``Widget`` *is* a ``BaseWidget`` but has the
methods of the traits ``Pack``, ``Place`` and ``Grid``.

One could take even a more extreme stance: abolish both multiple inheritance
and traits and add methods to a class by hand, which something
like this:

.. code-block:: python

  def include_methods(cls, method_container):
     for name, value in vars(method_container).itervalues():
         setattr(cls, name, value)

This approach is definitely simple, but perhaps *too* simple.  A nice
property of inheritance is that if you have a class ``C`` inheriting
from class ``M`` and you change a method in ``M`` at runtime, after
``C`` has been created and instantiated, automagically all instances
of ``C`` gets the new version of the method, which is pretty useful
for debugging purposes. This feature is lost in the simplistic
approach but not lost in the trait approach: the trait implementation
is fully dynamic and if you change the mixin the instances will be
changed too. This however, is a minor point. 

The real reason why just
including methods in the class namespace is a bad idea is that it
easily leads you into the problem of *namespace pollution*. I have
discussed the issue elsewhere_: if you keep injecting methods into a
class (both directly or via inheritance) you may end up having
hundreds of methods flattened at the same level. A picture is worth a
thousand words, so have a look at the `PloneSite hierarchy`_ if you
want to understand the horror I would like to avoid with traits
(the picture shows the number of
nonspecial attributes defined per class in square brackets): in
the Plone Site hierarchy there
are 38 classes, 88 overridden names, 42 special names, 648
non-special attributes and methods. It is a maintenance nighmare.
The
``mtrait`` implementation avoids name space pollution since each trait
has its own namespace and you are guaranteed against name
conflicts. Moreover, introspecting traits is much easier than
introspecting inheritance hierarchies: even the autocompletion
feature works best.

*Note 1*: even if I think that a language would be better off without
full multiple inheritance with cooperative methods, that does not mean
that I am against interfaces. I think a class should be able to
implement multiple interfaces at the same time, but this has nothing
to do with multiple inheritance. However in Python (starting from
Python 2.6) multiple inheritance is abused to simulate interface
requirements so that if you want to implement many interfaces at the
same time you are suggested to inherit from many abstract base classes
at the same time.  In an ideal language without multiple inheritance
you could just add an ``__interfaces__`` attribute to classes. In an
ideal language interfaces would be abstract objects (a little more
than a list of names) whereas in Python they are concrete classes and
that explains why you inherit from them.

.. _current super in Python: http://www.artima.com/weblogs/viewpost.jsp?thread=236275
.. _elsewhere: http://stacktrace.it/articoli/2008/06/i-pericoli-della-programmazione-con-i-mixin1/
.. _PloneSite hierarchy: http://www.phyast.pitt.edu/~micheles/python/plone-hierarchy.png 

Future work
--------------------------------------------------------

The Smalltalk implementation of traits provides method renaming 
out of the box. The Python implementation has no facilities in
this sense. In the future I may decide to give some support for
renaming, or I may not. At the present you can just rename
your methods by hand.
Also, in the future I may decide to add some kind of adaptation mechanism
or I may not: after all the primary goal of this implementation is semplicity
and I don't want to clutter it with too many features.

I am very open to feedback and criticism: I am releasing this module
with the hope that it will be used in real life situations to gather
experience with the traits concept. Clearly I am not proposing that
Python should remove multiple inheritance in favor of traits. I am
just looking for a few adventurous volunteers wanting to experiment
with traits; if the experiment goes well, and people start using
(multiple) inheritance less than they do now, I will be happy. 

The point I am trying to make is that Python is not Java: whereas in
Java you have very little alternatives to inheritance, in Python you
have lots of alternatives involving composition and it is worth to
try them out.

*Post Scriptum:* if your are curious about the origin of the letter ``m`` in
``mtrait``, I added it to avoid conflicts with other traits
implementation; it stands for *Meta* or for *My* or even for
*Michele*, at your will ;)

.. _traits: http://www.iam.unibe.ch/~scg/Research/Traits/
.. _Traits - Composable Units of Behavior: http://www.iam.unibe.ch/%7Escg/Archive/Papers/Scha03aTraits.pdf
.. _PLT Scheme: http://www.cs.utah.edu/plt/publications/aplas06-fff.pdf

"""
import cPickle as pickle
from mtrait import *
from Tkinter import *

class TOSWidget(BaseWidget):
    include(Pack, Place, Grid)
    info = Pack.info.im_func
    config = Pack.config.im_func
    configure = Pack.configure.im_func
    slaves = Pack.slaves.im_func
    forget = Pack.forget.im_func
    propagate = Pack.propagate.im_func

class TOSWidget2(BaseWidget):
    __mixins__ = Pack, Place, Grid
    __metaclass__ = TOSMeta
    info = Pack.info.im_func
    config = Pack.config.im_func
    configure = Pack.configure.im_func
    slaves = Pack.slaves.im_func
    forget = Pack.forget.im_func
    propagate = Pack.propagate.im_func

label = TOSWidget(master=None, widgetName='label', cnf=dict(text="hello"))

pickle.dumps(label)

class HTTP(object):
    def GET(self):
        print 'calling HTTP.GET from %s' % self
    def POST(self):
        print 'calling HTTP.POST from %s' % self
    @classmethod
    def cm(cls):
        print 'calling HTTP.cm from %s' % cls

class FTP(object):
    def SEND(self):
        print 'calling FTP.SEND from %s' % self
    def RECV(self):
        print 'calling FTP.RECV from %s' % self
    @staticmethod
    def sm():
        print 'calling staticmethod'

class Mixin(object):
    def _helper(self):
        return 1
    def method(self):
        return self._helper() + 1

class MyMetacls(type):
    "A do-nothing metaclass for exemplification purposes"

def test_getattr():
    try:
        class C:
            __metaclass__ = TOSMeta
            def __getattribute__(self, name):
                pass
    except OverridingError: # expected
        pass
    else:
        raise RuntimeError("OverridingError not raised!")
    
def test_multi_include():
    class B(object):
        __metaclass__ = MyMetacls
        include(FTP)
    class C(B):
        include(HTTP)
        def __init__(self, a):
            self.a = a
    class D(C):
        pass
    x = D(1)
    x.a
    x.cm()
    x.sm()
    print type(B), type(C)

def test_Trait_pickle():
    t = Trait(Mixin, Mixin.__name__)
    pickle.loads(pickle.dumps(t))

class C:
    "An example not using include"
    __metaclass__ = TOSMeta
    __mixins__ = HTTP, FTP, Mixin

def setup():
    global c
    c = C()
    print c

class A(object):
    def save(self):
        print 'A.save'

class AM(A):
    include(Mixin)

assert AM().method() == 2

class B(A):
    def save(self):
        print 'B.save'
        super(B, self).save()

class C(A):
    def save(self):
        print 'C.save'
        A.save(self)

class D(B, C):
    def save(self):
        print 'D.save'
        super(D, self).save()

C.C_save = C.save.im_func

class E(B):
    include(C)
    def save(self):
        print 'D.save'
        print 'C.save'
        B.save(self)

check_overridden([B, C], set(), raise_="warning")

d = D()
e = E()
e.save()

if __name__ == '__main__':
    import doctest; doctest.testmod()
    try:        
        import nose
    except ImportError:
        pass
    else:
        nose.runmodule()

'''
The class decorator ``include`` takes a set of mixin
classes in input, determines the right metaclass to use and returns a
class in output; the metaclass enhances the class in output by adding
to it a special attribute ``__mixins__``, a descriptor ``__traits__``, a custom
``__getattribute__`` method and a custom ``__getstate__`` method. The
``__mixins__`` attribute is just the tuple of the included mixins;
the ``__traits__`` descriptor is a ``TraitContainer`` object akin to a
dictionary of ``Trait`` objects (one for each mixin class); the
``__getattribute__`` method allows automatic dispatching to them; the
``__getstate__`` method is needed to preserve pickleability of the
instances - assuming the original class before decoration was
pickleable, of course.

Are there disadvantages of my proposed trait implementation with
respect to multiple inheritance? I don't think there are serious
disadvantages, since you can always work around them.
*Multiple inheritance is most useful at the metaclass level,
where you need to cooperatively override __new__!*

from ms.tools.conceptualmap import *
PloneSite=app.rcare.__class__
plot_classes(PloneSite.mro(), verbose=2)


'''