def testModifyDefinition(self):
        defn = MapClassDefinition('definition')
        defn.add_item('key1', 'Display Name', 'Short Name')
        defn.add_item('key2', 'Display Name 2')

        mc1 = defn.map_class()

        defn.add_item('key3', 'Display Name 3')

        mc2 = defn.map_class()

        # Map Classes have the right keys:
        self.assertEqual(mc1.keys(), ['key1', 'key2'])
        self.assertEqual(mc2.keys(), ['key1', 'key2', 'key3'])

        # Definitions from MapClasses have the right keys & display names:
        def1 = mc1.definition()
        self.assertEqual(def1.name, 'definition')
        self.assertEqual(def1.keys, ['key1', 'key2'])
        self.assertEqual(def1.display_name('key1'), 'Display Name')
        self.assertEqual(def1.display_name('key2'), 'Display Name 2')
        self.assertEqual(def1.display_short_name('key1'), 'Short Name')
        self.assertTrue(def1.display_short_name('key2') is None)

        def2 = mc2.definition()
        self.assertEqual(def2.name, 'definition')
        self.assertEqual(def2.keys, ['key1', 'key2', 'key3'])
        self.assertEqual(def2.display_name('key1'), 'Display Name')
        self.assertEqual(def2.display_name('key2'), 'Display Name 2')
        self.assertEqual(def2.display_name('key3'), 'Display Name 3')
        self.assertEqual(def2.display_short_name('key1'), 'Short Name')
        self.assertTrue(def2.display_short_name('key2') is None)
        self.assertTrue(def2.display_short_name('key3') is None)
    def testViews(self):
        if not hasattr(dict, 'viewkeys'):
            print 'Skipping view tests'
            return

        defn = MapClassDefinition('class-name')
        defn.add_item('alpha', 'Alpha')
        defn.add_item('beta', 'Beta')
        defn.add_item('gamma', 'Gamma')
        defn.add_item('delta', 'Delta')

        mc = defn.map_class(alpha='the first', beta='the second',
                            gamma='the third', delta='the fourth')
        keys = mc.viewkeys()
        values = mc.viewvalues()
        items = mc.viewitems()

        # __len__
        self.assertEqual(len(keys), 4)
        self.assertEqual(len(values), 4)
        self.assertEqual(len(items), 4)

        # __contains__
        self.assertTrue('alpha' in keys)
        self.assertTrue('the second' in values)
        self.assertTrue(('gamma', 'the third') in items)

        self.assertFalse('omega' in keys)
        self.assertFalse('the fifth' in values)
        self.assertFalse(('omega', 'the fifth') in items)

        self.assertTrue('omega' not in keys)
        self.assertTrue('the fifth' not in values)
        self.assertTrue(('omega', 'the fifth') not in items)

        self.assertFalse('alpha' not in keys)
        self.assertFalse('the second' not in values)
        self.assertFalse(('gamma', 'the third') not in items)

        # __and__
        self.assertEqual(
            keys & ['alpha', 'gamma', 'omega'],
            set(['alpha', 'gamma'])
        )
        self.assertEqual(
            items & [('beta', 'the second'), 3],
            set([('beta', 'the second')])
        )

        # __or__
        self.assertEqual(
            keys | ['omega'],
            set(['alpha', 'beta', 'gamma', 'delta', 'omega'])
        )
        self.assertEqual(
            items | [10],
            set([('alpha', 'the first'), ('beta', 'the second'),
                 ('gamma', 'the third'), ('delta', 'the fourth'), 10])
        )

        # __sub__
        self.assertEqual(
            keys - ['beta', 'omega'],
            set(['alpha', 'gamma', 'delta'])
        )
        self.assertEqual(
            items - [('beta', 'the second'), ('omega', 'not there')],
            set([('alpha', 'the first'), ('gamma', 'the third'),
                 ('delta', 'the fourth')])
        )

        # __xor__
        self.assertEqual(
            keys ^ ['beta', 'omega'],
            set(['alpha', 'gamma', 'delta', 'omega'])
        )
        self.assertEqual(
            items ^ [('beta', 'the second'), ('omega', 'the last')],
            set([('alpha', 'the first'), ('gamma', 'the third'),
                 ('delta', 'the fourth'), ('omega', 'the last')])
        )

        # no set ops for value views
        self.assertRaises(TypeError, operator.and_, values, set())
        self.assertRaises(TypeError, operator.or_, values, set())
        self.assertRaises(TypeError, operator.sub, values, set())
        self.assertRaises(TypeError, operator.xor, values, set())

        # no set ops for item views with unhashable values
        mc['delta'] = [1]
        self.assertRaises(TypeError, operator.and_, items, set())
        self.assertRaises(TypeError, operator.or_, items, set())
        self.assertRaises(TypeError, operator.sub, items, set())
        self.assertRaises(TypeError, operator.xor, items, set())

        # Changes during iteration
        value_it = iter(values)
        item_it = iter(items)
        self.assertEqual(value_it.next(), 'the first')
        self.assertEqual(value_it.next(), 'the second')
        self.assertEqual(item_it.next(), ('alpha', 'the first'))
        self.assertEqual(item_it.next(), ('beta', 'the second'))
        mc['gamma'] = '3rd'
        self.assertEqual(value_it.next(), '3rd')
        self.assertEqual(item_it.next(), ('gamma', '3rd'))
        mc['gamma'] = 'Third'
        mc['delta'] = 'Fourth'
        self.assertEqual(value_it.next(), 'Fourth')
        self.assertEqual(item_it.next(), ('delta', 'Fourth'))
    def testBasicMapClass(self):
        defn = MapClassDefinition('species')
        defn.add_item('common', 'Common Name', 'Common')
        defn.add_item('genus', 'Genus')
        defn.add_item('species', 'Species')
        # Duplicate key name:
        self.assertRaises(ValueError, defn.add_item, 'genus', 'Genus')

        # https://en.wikipedia.org/wiki/List_of_moths

        # A few different ways to populate items:
        atlas = defn.map_class()
        atlas['common'] = 'Atlas moth'
        atlas['genus'] = 'Attacus'
        atlas['species'] = 'atlas'

        gypsy = defn.map_class()
        gypsy.update(common='Gypsy moth', species='dispar', genus='Lymantria')

        peppered = defn.map_class(common='Peppered moth', genus='Biston',
                                  species='betularia')

        # Make definition from class:
        defn2 = peppered.definition()
        self.assertEqual(defn.keys, defn2.keys)

        # Use new definition:
        zea = defn2.map_class([('common', 'Corn earworm'),
                               ('genus', 'Helicoverpa'), ('species', 'zea')])

        # Modify:
        it = zea.iteritems()
        zea['common'] = 'Cotton bollworm'
        # Iterators pick up changes during iteration.
        self.assertEqual(it.next(), ('common', 'Cotton bollworm'))

        # Invalid modifications:
        self.assertRaises(NotImplementedError, atlas.__delitem__, 'common')
        self.assertRaises(NotImplementedError, atlas.clear)
        self.assertRaises(NotImplementedError, atlas.pop, 'common', None)
        self.assertRaises(NotImplementedError, atlas.popitem)
        self.assertRaises(KeyError, atlas.__setitem__, 'name', 'Mothra')
        self.assertRaises(KeyError, atlas.setdefault, 'name', 'Mothra')
        self.assertRaises(KeyError, atlas.update, name='Mothra')

        # Check key ordering:
        moths = [atlas, gypsy, peppered, zea]
        keys = ['common', 'genus', 'species']
        for moth in moths:
            self.assertEqual(list(moth), keys)
            self.assertEqual(list(moth.iterkeys()), keys)
            self.assertEqual(moth.keys(), keys)

        # Check value ordering:
        values = ['Cotton bollworm', 'Helicoverpa', 'zea']
        self.assertEqual(zea.values(), values)
        self.assertEqual(list(zea.itervalues()), values)

        # Check item ordering:
        items = [('common', 'Cotton bollworm'), ('genus', 'Helicoverpa'),
                 ('species', 'zea')]
        self.assertEqual(zea.items(), items)
        self.assertEqual(list(zea.iteritems()), items)

        # Same stuff for views, if supported.
        if hasattr(dict, 'viewitems'):
            for moth in moths:
                self.assertEqual(list(moth.viewkeys()), keys)
            self.assertEqual(list(zea.viewvalues()), values)
            self.assertEqual(list(zea.viewitems()), items)

        # Check long & short display names
        for moth in moths:
            self.assertEqual(moth.display_name('common'), 'Common Name')
            self.assertEqual(moth.display_name('genus'), 'Genus')
            self.assertEqual(moth.display_name('species'), 'Species')

            self.assertEqual(moth.display_short_name('common'), 'Common')
            self.assertTrue(moth.display_short_name('genus') is None)
            self.assertTrue(moth.display_short_name('species') is None)

        # Shallow copy:
        orig = defn.map_class(common=['Comet moth'], genus='Argema',
                              species='mittrei')
        cpy = orig.copy()

        self.assertEqual(type(orig), type(cpy))
        self.assertEqual(orig.items(), cpy.items())
        # Copy should be shallow
        orig['common'].append('Madagascan moon moth')
        self.assertEqual(orig.items(), cpy.items())