This file is indexed.

/usr/share/pyshared/martian/core.txt is in python-martian 0.12-0ubuntu2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

   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
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
Martian core
============

A simple framework
------------------

Let's look at a very simple framework where files are handled by their
extensions::

  >>> class filehandler(FakeModule):
  ...   import os
  ...
  ...   def handle_txt(filepath):
  ...     return "Text file"
  ...
  ...   def handle_xml(filepath):
  ...     return "XML file"
  ...
  ...   extension_handlers = { '.txt': handle_txt, '.xml': handle_xml }
  ...
  ...   def handle(filepath):
  ...      name, ext = os.path.splitext(filepath)
  ...      return extension_handlers[ext](filepath)
  >>> from martiantest.fake import filehandler

Now let's try the ``handle`` function for a few file types::

  >>> filehandler.handle('test.txt')
  'Text file'
  >>> filehandler.handle('test2.xml')
  'XML file'

File extensions that we do not recognize cause a ``KeyError`` to be
raised::

  >>> filehandler.handle('image.png')
  Traceback (most recent call last):
  ...
  KeyError: '.png'

We now want to plug into this filehandler framework and provide a
handler for ``.png`` files::

  >>> class pnghandler(FakeModule):
  ...    def handle_png(filepath):
  ...        return "PNG file"
  ...
  ...    filehandler.extension_handlers['.png'] = handle_png
  >>> from martiantest.fake import pnghandler

In the extension module, we manipulate the ``extension_handlers``
dictionary of the ``filehandler`` module and plug in our own
function. PNG handling works now::

  >>> filehandler.handle('image.png')
  'PNG file'

Grokkers that grok
------------------

Let's write a grokker that can grok the file types. A ``Grokker`` is
an object that can *grok* objects - execute configuration actions
pertaining to the grokked object, such as registering it with some
central registry. Different kinds of grokkers can grok different types
of objects (instances, classes, functions).

Let's define a Grokker to help us register the file type handler
functions as seen in our previous example::

  >>> import types
  >>> from zope.interface import implements
  >>> import martian
  >>> class FileTypeGrokker(martian.InstanceGrokker):
  ...   martian.component(types.FunctionType)
  ...
  ...   def grok(self, name, obj, **kw):
  ...     if not name.startswith('handle_'):
  ...       return False
  ...     ext = name.split('_')[1]
  ...     filehandler.extension_handlers['.' + ext] = obj
  ...     return True

This ``InstanceGrokker`` allows us to grok instances of a particular
type (such as functions). We need to define the type of object we're
looking for with the ``martian.component`` directive. In the ``grok``
method, we first make sure we only grok functions that have a name
that starts with ``handle_``. Then we determine the used extension
from the name and register the funcion in the ``extension_handlers``
dictionary of the ``filehandler`` module. We return ``True`` if we
indeed grokked the object.

An instance will provide the IGrokker interface::

  >>> filetype_grokker = FileTypeGrokker()
  >>> from martian.interfaces import IGrokker
  >>> IGrokker.providedBy(filetype_grokker)
  True

Now let's use the grokker to grok a new handle function::

  >>> def handle_jpg(filepath):
  ...   return "JPG file"
  >>> filetype_grokker.grok('handle_jpg', handle_jpg)
  True

After we grokked, we have registered a handler for ``.jpg`` files
(the extension to register under was deduced from the function name)::

  >>> sorted(filehandler.extension_handlers.keys())
  ['.jpg', '.png', '.txt', '.xml']

This means now our ``filehandler.handle`` function is now able to
handle JPG files as well::

  >>> filehandler.handle('image2.jpg')
  'JPG file'

If we try to grok a function that doesn't start with ``handle_`` in its
name, nothing will happen::

  >>> def something(filepath):
  ...   return 'Something'
  >>> filetype_grokker.grok('something', something)
  False
  >>> 'something' in filehandler.extension_handlers
  False

Grokking a module
-----------------

Grokking individual components is useful, but to make Martian really
useful we need to be able to grok whole modules or packages as well.
Let's look at a special grokker that can grok a Python module, the
``ModuleGrokker``.

The idea is that the ``ModuleGrokker`` groks any components in a
module that it recognizes. A ``ModuleGrokker`` does not work alone. It
needs to be supplied with one or more grokkers that can grok the
components to be founded in a module::

  >>> module_grokker = martian.ModuleGrokker()
  >>> module_grokker.register(filetype_grokker)

We now define a module that defines a few filetype handlers to be
grokked::

  >>> class lotsofhandlers(FakeModule):
  ...   def handle_exe(filepath):
  ...     return "EXE file"
  ...
  ...   def handle_ogg(filepath):
  ...     return "OGG file"
  ...
  ...   def handle_svg(filepath):
  ...     return "SVG file"
  >>> from martiantest.fake import lotsofhandlers

Let's grok it::

  >>> module_grokker.grok('lotsofhandlers', lotsofhandlers)
  True

The new registrations are now available::

  >>> sorted(filehandler.extension_handlers.keys())
  ['.exe', '.jpg', '.ogg', '.png', '.svg', '.txt', '.xml']

The system indeed recognizes them now::

  >>> filehandler.handle('test.ogg')
  'OGG file'
  >>> filehandler.handle('test.svg')
  'SVG file'
  >>> filehandler.handle('test.exe')
  'EXE file'

As you can see, with Martian we can now define handlers without ever
having to register them manually. This allows us to rewrite our
original module and take out the manual registrations completely::

  >>> class filehandler(FakeModule):
  ...   import os
  ...
  ...   def handle_txt(filepath):
  ...     return "Text file"
  ...
  ...   def handle_xml(filepath):
  ...     return "XML file"
  ...
  ...   extension_handlers = {}
  ...
  ...   def handle(filepath):
  ...      name, ext = os.path.splitext(filepath)
  ...      return extension_handlers[ext](filepath)
  >>> from martiantest.fake import filehandler

Let's use martian to do the registrations for us::

  >>> module_grokker.grok('filehandler', filehandler)
  True
  >>> filehandler.handle('test.txt')
  'Text file'

InstanceGrokker
---------------

We have seen how to grok module-level functions. Let's now grok some
other kind of instance, a ``Color``::

  >>> class color(FakeModule):
  ...   class Color(object):
  ...     def __init__(self, r, g, b):
  ...       self.r = r
  ...       self.g = g
  ...       self.b = b
  ...     def __repr__(self):
  ...       return '<Color %s %s %s>' % (self.r, self.g, self.b)
  ...   all_colors = {}
  >>> from martiantest.fake import color

We now want a grokker that can recognize colors and put them in the
``all_colors`` dictionary, with the names as the keys, and the color
object as the values. We can use ``InstanceGrokker`` to construct it::

  >>> class ColorGrokker(martian.InstanceGrokker):
  ...   martian.component(color.Color)
  ...   def grok(self, name, obj, **kw):
  ...     color.all_colors[name] = obj
  ...     return True

Let's create ``color_grokker`` and grok a color::

  >>> color_grokker = ColorGrokker()
  >>> black = color.Color(0, 0, 0) # we DO consider black as a color :)
  >>> color_grokker.grok('black', black)
  True

It ends up in the ``all_colors`` dictionary::

  >>> color.all_colors
  {'black': <Color 0 0 0>}

If we put ``color_grokker`` into a ``ModuleGrokker``, we can now grok
multiple colors in a module::

  >>> Color = color.Color
  >>> class colors(FakeModule):
  ...   red = Color(255, 0, 0)
  ...   green = Color(0, 255, 0)
  ...   blue = Color(0, 0, 255)
  ...   white = Color(255, 255, 255)
  >>> from martiantest.fake import colors
  >>> colors_grokker = martian.ModuleGrokker()
  >>> colors_grokker.register(color_grokker)
  >>> colors_grokker.grok('colors', colors)
  True
  >>> sorted(color.all_colors.items())
  [('black', <Color 0 0 0>),
   ('blue', <Color 0 0 255>),
   ('green', <Color 0 255 0>),
   ('red', <Color 255 0 0>),
   ('white', <Color 255 255 255>)]

Subclasses of ``Color`` are also grokked::

  >>> class subcolors(FakeModule):
  ...   class SpecialColor(Color):
  ...     pass
  ...   octarine = SpecialColor(-255, 0, -255)
  >>> from martiantest.fake import subcolors
  >>> colors_grokker.grok('subcolors', subcolors)
  True
  >>> 'octarine' in color.all_colors
  True

MultiInstanceGrokker
--------------------

In the previous section we have created a particular grokker that
looks for instances of a component class, in this case
``Color``. Let's introduce another ``InstanceGrokker`` that looks for
instances of ``Sound``::

  >>> class sound(FakeModule):
  ...   class Sound(object):
  ...     def __init__(self, desc):
  ...       self.desc = desc
  ...     def __repr__(self):
  ...       return '<Sound %s>' % (self.desc)
  ...   all_sounds = {}
  >>> from martiantest.fake import sound

  >>> class SoundGrokker(martian.InstanceGrokker):
  ...   martian.component(sound.Sound)
  ...   def grok(self, name, obj, **kw):
  ...     sound.all_sounds[name] = obj
  ...     return True
  >>> sound_grokker = SoundGrokker()

What if we now want to look for ``Sound`` and ``Color`` instances at
the same time? We have to use the ``color_grokker`` and
``sound_grokker`` at the same time, and we can do this with a
``MultiInstanceGrokker``::

  >>> from martian.core import MultiInstanceGrokker
  >>> multi_grokker = MultiInstanceGrokker()
  >>> multi_grokker.register(color_grokker)
  >>> multi_grokker.register(sound_grokker)

Let's grok a new color with our ``multi_grokker``::

  >>> grey = Color(100, 100, 100)
  >>> multi_grokker.grok('grey', grey)
  True
  >>> 'grey' in color.all_colors
  True

Let's grok a sound with our ``multi_grokker``::

  >>> moo = sound.Sound('Moo!')
  >>> multi_grokker.grok('moo', moo)
  True
  >>> 'moo' in sound.all_sounds
  True

We can also grok other objects, but this will have no effect::

  >>> something_else = object()
  >>> multi_grokker.grok('something_else', something_else)
  False

Let's put our ``multi_grokker`` in a ``ModuleGrokker``. We can do
this by passing it explicitly to the ``ModuleGrokker`` factory::

  >>> module_grokker = martian.ModuleGrokker(grokker=multi_grokker)

We can now grok a module for both ``Color`` and ``Sound`` instances::

  >>> Sound = sound.Sound
  >>> class lightandsound(FakeModule):
  ...   dark_red = Color(150, 0, 0)
  ...   scream = Sound('scream')
  ...   dark_green = Color(0, 150, 0)
  ...   cheer = Sound('cheer')
  >>> from martiantest.fake import lightandsound
  >>> module_grokker.grok('lightandsound', lightandsound)
  True
  >>> 'dark_red' in color.all_colors
  True
  >>> 'dark_green' in color.all_colors
  True
  >>> 'scream' in sound.all_sounds
  True
  >>> 'cheer' in sound.all_sounds
  True

ClassGrokker
------------

Besides instances we can also grok classes. Let's define an
application where we register classes representing animals.  Animals
can be given names using the ``name`` directive::

  >>> from martian.directive import Directive, CLASS, ONCE

  >>> class animal(FakeModule):
  ...   class name(Directive):
  ...     scope = CLASS
  ...     store = ONCE
  ...
  ...   class Animal(object):
  ...     def __repr__(self):
  ...       return '<Animal %s>' % animal.name.bind().get(self)
  ...
  ...   all_animals = {}
  ...   def create_animal(name):
  ...     return all_animals[name]()
  >>> from martiantest.fake import animal

Let's define a grokker that can grok an ``Animal``.  We could either
implement the ``grok`` method as with ``InstanceGrokkers``, or we can
rely on the implementation that the baseclass already provides.  In
the latter case, we just have to declare what directives the grokker
may want to use on the class and the implement the ``execute`` method::

  >>> class AnimalGrokker(martian.ClassGrokker):
  ...   martian.component(animal.Animal)
  ...   martian.directive(animal.name)
  ...   def execute(self, class_, name, **kw):
  ...     animal.all_animals[name] = class_
  ...     return True

Let's test our grokker::

  >>> class Snake(animal.Animal):
  ...   animal.name('snake')
  ...
  >>> animal_grokker = AnimalGrokker()
  >>> animal_grokker.grok('Snake', Snake)
  True
  >>> animal.all_animals.keys()
  ['snake']

We can create a snake now::

  >>> animal.create_animal('snake')
  <Animal snake>

Note that we can supply a different default value for the directive
default::

  >>> class AnimalGrokker(AnimalGrokker):
  ...   martian.directive(animal.name, default='generic animal')
  ...
  >>> class Generic(animal.Animal):
  ...   pass
  ...
  >>> animal_grokker = AnimalGrokker()
  >>> animal_grokker.grok('Generic', Generic)
  True
  >>> sorted(animal.all_animals.keys())
  ['generic animal', 'snake']

Moreover, we can also supply a default factory that may want to
determine a dynamic default value based on the class that's being
grokked.  For instance, let's say the default name of an animal should
the class name converted to lowercase letters::

  >>> def default_animal_name(class_, module, **data):
  ...   return class_.__name__.lower()
  ...
  >>> class AnimalGrokker(AnimalGrokker):
  ...   martian.directive(animal.name, get_default=default_animal_name)
  ...
  >>> class Mouse(animal.Animal):
  ...   pass
  ...
  >>> animal_grokker = AnimalGrokker()
  >>> animal_grokker.grok('Mouse', Mouse)
  True
  >>> sorted(animal.all_animals.keys())
  ['generic animal', 'mouse', 'snake']

Note that these default value factories will also get the data from
all directives that are in front of them in the grokker's directive
list.  For instance, consider the following directive:

  >>> class zoologicalname(animal.name):
  ...   pass
  ...

with the following default rule that takes the regular name as the
default zoological name::

  >>> def default_zoological_name(class_, module, name, **data):
  ...   return name
  ...
  >>> class ZooAnimalGrokker(martian.ClassGrokker):
  ...   martian.component(animal.Animal)
  ...   martian.directive(animal.name, get_default=default_animal_name)
  ...   martian.directive(zoologicalname, get_default=default_zoological_name)
  ...
  ...   def execute(self, class_, name, zoologicalname, **kw):
  ...     print zoologicalname
  ...     return True
  ...
  >>> class Hippopotamus(animal.Animal):
  ...   pass
  ...   # No need to use animal.name(), we'll take the class name as default.
  ...   # The zoological name is the same as well.
  ...

  >>> zoo_animal_grokker = ZooAnimalGrokker()
  >>> zoo_animal_grokker.grok('Hippopotamus', Hippopotamus)
  hippopotamus
  True

If you pass a non-directive to ``martian.directive``, you get an error::

  >>> class Test(martian.ClassGrokker):
  ...    martian.directive('foo')
  Traceback (most recent call last):
  GrokImportError: The 'directive' directive can only be called with a directive.

MethodGrokker
-------------

A special kind of class grokker is the ``MethodGrokker``.  It inspects
the class at hand and calls ``execute`` for each *method* the class
provides.

Consider the following baseclass for circus animals:

  >>> class CircusAnimal(animal.Animal):
  ...   def begin_show(self):
  ...     pass
  ...   def end_show(self):
  ...      pass

Circus animals define lots of methods which we'll collect using this
grokker:

  >>> circus_animals = {}
  >>> from martian import MethodGrokker
  >>> class CircusAnimalGrokker(MethodGrokker):
  ...   martian.component(CircusAnimal)
  ...   def execute(self, class_, method, **kw):
  ...     circus_animals.setdefault(class_.__name__, []).append(method.__name__)
  ...     return True
  ...

Now consider the following circus animals:

  >>> class Monkey(CircusAnimal):
  ...   def climb(self):
  ...     pass
  ...   def _take_dump(self):
  ...     pass
  ...
  >>> class Camel(CircusAnimal):
  ...   def walk(self):
  ...     pass
  ...   def spit(self):
  ...     pass

  >>> circus_animal_grokker = CircusAnimalGrokker()
  >>> circus_animal_grokker.grok('Monkey', Monkey)
  True
  >>> circus_animal_grokker.grok('Camel', Camel)
  True

Let's look at the results:

  >>> for circus_animal, methods in sorted(circus_animals.items()):
  ...     print "%s can %s." % (circus_animal, " and ".join(sorted(methods)))
  ...
  Camel can spit and walk.
  Monkey can climb.

As we see, private methods (those beginning with underscores) have
been ignored.  Furthermore, methods inherited from the component
baseclass (in this case ``CircusAnimal``) have also been ignored.

If we wrote a class without any methods, we would encounter an error:

  >>> class Snail(CircusAnimal):
  ...   pass

  >>> circus_animal_grokker.grok('Snail', Snail)
  Traceback (most recent call last):
    ...
  GrokError: <class 'Snail'> does not define any public
  methods. Please add methods to this class to enable its
  registration.

MultiClassGrokker
-----------------

We now want to be able to grok the following module and have the
``Animal`` subclasses (but not the ``Chair`` class, which is not an
animal) automatically become available::

  >>> class animals(FakeModule):
  ...   class Elephant(animal.Animal):
  ...     animal.name('elephant')
  ...   class Tiger(animal.Animal):
  ...     animal.name('tiger')
  ...   class Lion(animal.Animal):
  ...     animal.name('lion')
  ...   class Chair(object):
  ...     animal.name('chair')
  >>> from martiantest.fake import animals

First we need to wrap our ``AnimalGrokker`` into a ``MultiClassGrokker``::

 >>> from martian.core import MultiClassGrokker
 >>> multi_grokker = MultiClassGrokker()
 >>> multi_grokker.register(animal_grokker)

Now let's wrap it into a ``ModuleGrokker`` and grok the module::

  >>> grokker = martian.ModuleGrokker(grokker=multi_grokker)
  >>> grokker.grok('animals', animals)
  True

The animals (but not anything else) should have become available::

  >>> sorted(animal.all_animals.keys())
  ['elephant', 'generic animal', 'lion', 'mouse', 'snake', 'tiger']

We can create animals using their name now::

  >>> animal.create_animal('elephant')
  <Animal elephant>
  >>> animal.create_animal('tiger')
  <Animal tiger>

MultiGrokker
------------

``MultiInstanceGrokker`` and ``MultiClassGrokker`` can grok instances
and classes respectively, but a ``MultiInstanceGrokker`` won't work
correctly if it runs into a class and vice versa. For that we use a
``MultiGrokker``, which can deal with the full range of objects that
can be grokked, and skips those it doesn't recognize.

Let's fill a ``MultiGrokker`` with a bunch of grokkers::

  >>> from martian import MultiGrokker
  >>> multi = MultiGrokker()
  >>> multi.register(filetype_grokker)
  >>> multi.register(color_grokker)
  >>> multi.register(sound_grokker)
  >>> multi.register(animal_grokker)

Let's try it with some individual objects::

  >>> class Whale(animal.Animal):
  ...    animal.name('whale')
  >>> multi.grok('Whale', Whale)
  True
  >>> 'whale' in animal.all_animals
  True

This should have no effect, but not fail::

  >>> my_whale = Whale()
  >>> multi.grok('my_whale', my_whale)
  False

Grokked by the ColorGrokker::

  >>> multi.grok('dark_grey', Color(50, 50, 50))
  True
  >>> 'dark_grey' in color.all_colors
  True

Grokked by the SoundGrokker::

  >>> multi.grok('music', Sound('music'))
  True
  >>> 'music' in sound.all_sounds
  True

Not grokked::

  >>> class RockMusic(Sound):
  ...   pass
  >>> multi.grok('RockMusic', RockMusic)
  False

Grokked by SoundGrokker::

  >>> multi.grok('rocknroll', RockMusic('rock n roll'))
  True
  >>> 'rocknroll' in sound.all_sounds
  True

Not grokked::

  >>> class Chair(object):
  ...   pass
  >>> multi.grok('Chair', Chair)
  False

Grokked by ``filetype_grokker``::

  >>> def handle_py(filepath):
  ...   return "Python file"
  >>> multi.grok('handle_py', handle_py)
  True
  >>> '.py' in filehandler.extension_handlers
  True

Not grokked:

  >>> def foo():
  ...   pass
  >>> multi.grok('foo', foo)
  False

Not grokked either::

  >>> another = object()
  >>> multi.grok('another', another)
  False

Let's make a module which has a mixture between classes and instances,
some of which can be grokked::

  >>> class mix(FakeModule):
  ...   # grokked by AnimalGrokker
  ...   class Whale(animal.Animal):
  ...      animal.name('whale')
  ...   # not grokked
  ...   my_whale = Whale()
  ...   # grokked by ColorGrokker
  ...   dark_grey = Color(50, 50, 50)
  ...   # grokked by SoundGrokker
  ...   music = Sound('music')
  ...   # not grokked
  ...   class RockMusic(Sound):
  ...      pass
  ...   # grokked by SoundGrokker
  ...   rocknroll = RockMusic('rock n roll')
  ...   # grokked by AnimalGrokker
  ...   class Dragon(animal.Animal):
  ...     animal.name('dragon')
  ...   # not grokked
  ...   class Chair(object):
  ...     pass
  ...   # grokked by filetype_grokker
  ...   def handle_py(filepath):
  ...     return "Python file"
  ...   # not grokked
  ...   def foo():
  ...     pass
  ...   # grokked by AnimalGrokker
  ...   class SpermWhale(Whale):
  ...     animal.name('sperm whale')
  ...   # not grokked
  ...   another = object()
  >>> from martiantest.fake import mix

Let's construct a ``ModuleGrokker`` that can grok this module::

  >>> mix_grokker = martian.ModuleGrokker(grokker=multi)

Note that this is actually equivalent to calling ``ModuleGrokker``
without arguments and then calling ``register`` for the individual
``ClassGrokker`` and ``InstanceGrokker`` objects.

Before we do the grokking, let's clean up our registration
dictionaries::

  >>> filehandler.extension_handlers = {}
  >>> color.all_colors = {}
  >>> sound.all_sounds = {}
  >>> animal.all_animals = {}

Now we grok::

  >>> mix_grokker.grok('mix', mix)
  True
  >>> sorted(filehandler.extension_handlers.keys())
  ['.py']
  >>> sorted(color.all_colors.keys())
  ['dark_grey']
  >>> sorted(sound.all_sounds.keys())
  ['music', 'rocknroll']
  >>> sorted(animal.all_animals.keys())
  ['dragon', 'sperm whale', 'whale']

GlobalGrokker
-------------

Sometimes you want to let a grok action happen for each module. The
grok action could for instance read the globals of a module, or even
static files associated with the module by name. Let's create a module
with some global value::

  >>> class g(FakeModule):
  ...   amount = 50
  >>> from martiantest.fake import g

Now let's create a ``GlobalGrokker`` that reads ``amount`` and stores
it in the ``read_amount`` dictionary::

  >>> read_amount = {}
  >>> from martian import GlobalGrokker
  >>> class AmountGrokker(GlobalGrokker):
  ...   def grok(self, name, module, **kw):
  ...     read_amount[None] = module.amount
  ...     return True

Let's construct a ``ModuleGrokker`` with this ``GlobalGrokker`` registered::

  >>> grokker = martian.ModuleGrokker()
  >>> grokker.register(AmountGrokker())

Now we grok and should pick up the right value::

  >>> grokker.grok('g', g)
  True
  >>> read_amount[None]
  50

Old-style class support
-----------------------

So far we have only grokked either new-style classes or instances of
new-style classes. It is also possible to grok old-style classes and
their instances::

  >>> class oldstyle(FakeModule):
  ...   class Machine:
  ...     pass
  ...   all_machines = {}
  ...   all_machine_instances = {}
  >>> from martiantest.fake import oldstyle

Let's make a grokker for the old style class::

  >>> class MachineGrokker(martian.ClassGrokker):
  ...   martian.component(oldstyle.Machine)
  ...   def grok(self, name, obj, **kw):
  ...     oldstyle.all_machines[name] = obj
  ...     return True

And another grokker for old style instances::

  >>> class MachineInstanceGrokker(martian.InstanceGrokker):
  ...   martian.component(oldstyle.Machine)
  ...   def grok(self, name, obj, **kw):
  ...     oldstyle.all_machine_instances[name] = obj
  ...     return True

The multi grokker should succesfully grok the old-style ``Machine`` class
and instances of it::

  >>> multi = MultiGrokker()
  >>> multi.register(MachineGrokker())
  >>> multi.register(MachineInstanceGrokker())
  >>> class Robot(oldstyle.Machine):
  ...   pass
  >>> multi.grok('Robot', Robot)
  True
  >>> oldstyle.all_machines.keys()
  ['Robot']
  >>> robot = Robot()
  >>> multi.grok('robot', robot)
  True
  >>> oldstyle.all_machine_instances.keys()
  ['robot']

Grokking a package
------------------

A package consists of several sub modules. When grokking a package,
all the files in the package will be grokked. Let's first create a simple
grokker for the ``Animal`` class defined by the package::

  >>> from martian.tests.testpackage import animal
  >>> all_animals = {}
  >>> class AnimalGrokker(martian.ClassGrokker):
  ...   martian.component(animal.Animal)
  ...   def grok(self, name, obj, **kw):
  ...     all_animals[name] = obj
  ...     return True

The grokker will collect animals into the ``all_animals`` dictionary.

Let's register this grokker for a ModuleGrokker::

  >>> module_grokker = martian.ModuleGrokker()
  >>> module_grokker.register(AnimalGrokker())

Now let's grok the whole ``testpackage`` for animals::

  >>> from martian import grok_dotted_name
  >>> grok_dotted_name('martian.tests.testpackage', grokker=module_grokker)

We should now get some animals::

  >>> sorted(all_animals.keys())
  ['Animal', 'Bear', 'Dragon', 'Lizard', 'Python', 'SpermWhale', 'Whale']

Preparation and finalization
----------------------------

Before grokking a module, it may be that we need to do some
preparation. This preparation can include setting up some parameters
to pass along to the grokking process, for instance. We can pass
a ``prepare`` function a the ModuleGrokker::

  >>> class Number(object):
  ...   def __init__(self, nr):
  ...     self.nr = nr
  >>> all_numbers = {}
  >>> class NumberGrokker(martian.InstanceGrokker):
  ...  martian.component(Number)
  ...  def grok(self, name, obj, multiplier, **kw):
  ...    all_numbers[obj.nr] = obj.nr * multiplier
  ...    return True
  >>> def prepare(name, module, kw):
  ...   kw['multiplier'] = 3
  >>> module_grokker = martian.ModuleGrokker(prepare=prepare)
  >>> module_grokker.register(NumberGrokker())

We have created a ``prepare`` function that does one thing: create a
``multiplier`` parameter that is passed along the grokking
process. The ``NumberGrokker`` makes use of this to prepare the
``all_numbers`` dictionary values.

Let's try this with a module::

  >>> class numbers(FakeModule):
  ...   one = Number(1)
  ...   two = Number(2)
  ...   four = Number(4)
  >>> from martiantest.fake import numbers
  >>> module_grokker.grok('numbers', numbers)
  True
  >>> sorted(all_numbers.items())
  [(1, 3), (2, 6), (4, 12)]

You can also optionally register a finalization function, which will
be run at the end of a module grok::

  >>> def finalize(name, module, kw):
  ...     all_numbers['finalized'] = True
  >>> module_grokker = martian.ModuleGrokker(prepare=prepare, finalize=finalize)
  >>> module_grokker.register(NumberGrokker())
  >>> all_numbers = {}
  >>> module_grokker.grok('numbers', numbers)
  True
  >>> 'finalized' in all_numbers
  True

Sanity checking
---------------

Grokkers must return ``True`` if grokking succeeded, or ``False`` if
it didn't. If they return something else (typically ``None`` as the
programmer forgot to), the system will raise an error::

  >>> class BrokenGrokker(martian.InstanceGrokker):
  ...  martian.component(Number)
  ...  def grok(self, name, obj, **kw):
  ...    pass

  >>> module_grokker = martian.ModuleGrokker()
  >>> module_grokker.register(BrokenGrokker())
  >>> module_grokker.grok('numbers', numbers)
  Traceback (most recent call last):
    ...
  GrokError: <BrokenGrokker object at ...> returns None instead of
  True or False.

Let's also try this with a GlobalGrokker::

  >>> class MyGrokker(GlobalGrokker):
  ...   def grok(self, name, module, **kw):
  ...     return "Foo"
  >>> module_grokker = martian.ModuleGrokker()
  >>> module_grokker.register(MyGrokker())
  >>> module_grokker.grok('numbers', numbers)
  Traceback (most recent call last):
    ...
  GrokError: <MyGrokker object at ...> returns 'Foo' instead of True or False.

Meta Grokkers
-------------

Meta grokkers are grokkers that grok grokkers. This mechanism can be
used to extend Martian. Let's register a ``ClassMetaGrokker`` that
looks for subclasses of ``ClassGrokker``::

  >>> from martian.core import MetaGrokker
  >>> class ClassMetaGrokker(MetaGrokker):
  ...   martian.component(martian.ClassGrokker)
  >>> multi_grokker = MultiGrokker()
  >>> multi_grokker.register(ClassMetaGrokker(multi_grokker))

``multi_grokker`` should now grok subclasses of ``ClassGrokker``, such
as ``AnimalGrokker``::

  >>> all_animals = {} # clean out animal registry
  >>> multi_grokker.grok('AnimalGrokker', AnimalGrokker)
  True

Our multi_grokker should now also be able to grok animals::

  >>> class Woodpecker(animal.Animal):
  ...   pass
  >>> multi_grokker.grok('Woodpecker', Woodpecker)
  True

A ``MetaMultiGrokker`` is a ``MultiGrokker`` that comes preconfigured with
grokkers for ``ClassGrokker``, ``InstanceGrokker`` and ``GlobalGrokker``::

  >>> from martian import MetaMultiGrokker
  >>> multi_grokker = MetaMultiGrokker()

It works for ``ClassGrokker``::

  >>> all_animals = {}
  >>> multi_grokker.grok('AnimalGrokker', AnimalGrokker)
  True
  >>> multi_grokker.grok('Woodpecker', Woodpecker)
  True
  >>> all_animals
  {'Woodpecker': <class 'Woodpecker'>}

and for ``InstanceGrokker``::

  >>> color.all_colors = {}
  >>> multi_grokker.grok('ColorGrokker', ColorGrokker)
  True
  >>> multi_grokker.grok('color', Color(255, 0, 0))
  True
  >>> color.all_colors
  {'color': <Color 255 0 0>}

and for ``GlobalGrokker``::

  >>> read_amount = {}
  >>> multi_grokker.grok('AmountGrokker', AmountGrokker)
  True
  >>> grokker.grok('g', g)
  True
  >>> read_amount[None]
  50

We can clear the meta multi grokker::

  >>> multi_grokker.clear()

It won't grok particular classes or instances anymore::

  >>> multi_grokker.grok('Woodpecker', Woodpecker)
  False
  >>> multi_grokker.grok('color', Color(255, 0, 0))
  False

It can still grok grokkers::

  >>> multi_grokker.grok('ColorGrokker', ColorGrokker)
  True

Executing meta grokkers only once
---------------------------------

In case of ``ClassGrokker`` and all other grokkers that are grokked
by meta grokkers, we only want the grokking to occur once even if
the same module (or package) is grokked twice::

  >>> class TestOnce(object):
  ...   pass
  >>> executed = []
  >>> class somemodule(FakeModule):
  ...   class TestGrokker(martian.ClassGrokker):
  ...     martian.component(TestOnce)
  ...     def grok(self, name, obj, **kw):
  ...        executed.append(name)
  ...        return True
  >>> from martiantest.fake import somemodule
  >>> module_grokker = martian.ModuleGrokker(MetaMultiGrokker())

Let's grok the module once::

  >>> module_grokker.grok('somemodule', somemodule)
  True

Let's grok it twice::

  >>> module_grokker.grok('somemodule', somemodule)
  True

Even though we have grokked it twice, it is still only registered once. We
can show this by actually having it grok a ``TestOnce`` subclass::

  >>> class anothermodule(FakeModule):
  ...   class TestSub(TestOnce):
  ...      pass
  >>> from martiantest.fake import anothermodule
  >>> module_grokker.grok('anothermodule', anothermodule)
  True
  >>> executed
  ['TestSub']

This also works for instance grokkers::

  >>> class TestInstanceOnce(object):
  ...   pass
  >>> executed = []
  >>> class somemodule(FakeModule):
  ...   class TestGrokker(martian.InstanceGrokker):
  ...     martian.component(TestInstanceOnce)
  ...     def grok(self, name, obj, **kw):
  ...        executed.append(name)
  ...        return True
  >>> from martiantest.fake import somemodule
  >>> module_grokker.clear()
  >>> module_grokker.grok('somemodule', somemodule) # once
  True
  >>> module_grokker.grok('somemodule', somemodule) # twice
  True
  >>> class anothermodule(FakeModule):
  ...   test = TestInstanceOnce()
  >>> from martiantest.fake import anothermodule
  >>> module_grokker.grok('anothermodule', anothermodule)
  True
  >>> executed
  ['test']

It also works for global grokkers::

  >>> executed = []
  >>> class somemodule(FakeModule):
  ...   class TestGrokker(GlobalGrokker):
  ...     def grok(self, name, obj, **kw):
  ...       executed.append(name)
  ...       return True
  >>> from martiantest.fake import somemodule
  >>> module_grokker.clear()
  >>> module_grokker.grok('somemodule', somemodule) # once
  True
  >>> module_grokker.grok('somemodule', somemodule) # twice
  True

The second grokking will already make ``somemodule`` grokked::

  >>> executed
  ['somemodule']

Now let's grok another module::

  >>> class anothermodule(FakeModule):
  ...   pass
  >>> from martiantest.fake import anothermodule
  >>> module_grokker.grok('anothermodule', anothermodule)
  True
  >>> executed
  ['somemodule', 'anothermodule']

Priority
--------

When grokking a module using a ``ModuleGrokker``, grokker execution
can be determined by their priority. By default, grokkers have a
priority of ``0``. Let's define two base classes, ``A`` and ``B``,
which can be grokked::

  >>> class A(object):
  ...   pass

  >>> class B(object):
  ...   pass

Let's define a special kind of class grokker that records the order in
which names get grokked::

  >>> order = []
  >>> class OrderGrokker(martian.ClassGrokker):
  ...   def grok(self, name, obj, **kw):
  ...     order.append(name)
  ...     return True

Now we define two grokkers for subclasses of ``A`` and ``B``, where
the ``BGrokker`` has a higher priority::

  >>> class AGrokker(OrderGrokker):
  ...   martian.component(A)
  >>> class BGrokker(OrderGrokker):
  ...   martian.component(B)
  ...   martian.priority(10)

Let's register these grokkers::

  >>> multi_grokker = MetaMultiGrokker()
  >>> multi_grokker.grok('AGrokker', AGrokker)
  True
  >>> multi_grokker.grok('BGrokker', BGrokker)
  True

Let's create a module containing ``A`` and ``B`` subclasses::

  >>> class mymodule(FakeModule):
  ...   class ASub(A):
  ...     pass
  ...   class BSub(B):
  ...     pass
  >>> from martiantest.fake import mymodule

We'll grok it::

  >>> module_grokker = martian.ModuleGrokker(multi_grokker)
  >>> module_grokker.grok('mymodule', mymodule)
  True

Since the ``BGrokker`` has a higher priority, we expect the following
order of grokking::

  >>> order
  ['BSub', 'ASub']

This also works for GlobalGrokkers. We will define a GlobalGrokker
that has a higher priority than the default, but lower than B::

  >>> class MyGlobalGrokker(GlobalGrokker):
  ...   martian.priority(5)
  ...   def grok(self, name, obj, **kw):
  ...     order.append(name)
  ...     return True
  >>> multi_grokker.grok('MyGlobalGrokker', MyGlobalGrokker)
  True

We will grok the module again::

  >>> order = []
  >>> module_grokker.grok('mymodule', mymodule)
  True

This time, the global grokker should appear after 'BSub' but before 'ASub'::

  >>> order
  ['BSub', 'mymodule', 'ASub']


Module info
-----------

In addition to the ``name`` and ``object`` positional arguments,
grokkers will get also get a ``module_info`` keyword argument.  It is
an ``IModuleInfo`` object which can be used, for example, to query
module annotations.  Consider the following grokker:

  >>> from martian.error import GrokError
  >>> class AnnotationsGrokker(GlobalGrokker):
  ...   def grok(self, name, module, module_info, **kw):
  ...       ann = module_info.getAnnotation('some.annotation', None)
  ...       if ann is None:
  ...           raise GrokError('Did not find annotation!', module)
  ...       if ann != 'ME GROK SAY HI':
  ...           raise GrokError('Wrong annotation!', module)
  ...       return True

Now let's provide a fake module:

  >>> import new, sys
  >>> annotations = new.module('annotations')
  >>> annotations.__file__ = '/fake/module/annotations.py'
  >>> sys.modules['annotations'] = annotations

Clearly, it can't find the module-level variable yet:

  >>> module_grokker = martian.ModuleGrokker()
  >>> module_grokker.register(AnnotationsGrokker())
  >>> import martian
  >>> martian.grok_dotted_name('annotations', module_grokker)
  Traceback (most recent call last):
  ...
  GrokError: Did not find annotation!

Let's provide the annotation so that the grokker works as expected:

  >>> annotations.__some_annotation__ = 'ME GROK SAY HI'
  >>> martian.grok_dotted_name('annotations', module_grokker)

Finally clean up:

  >>> del sys.modules['annotations']