Skip to content
Browse files

Add unique ID to schemas by default.

This avoids that schemas are merged when a user forgets
to set the schema ID.
  • Loading branch information...
1 parent 1d46a07 commit 2daea8afdb3ce2d8aaf3317adc7a4ff4bfe813e0 @pberkes pberkes committed Dec 18, 2012
View
5 pyface/tasks/action/schema.py
@@ -2,7 +2,7 @@
from pyface.action.api import Action, ActionItem, Group, \
MenuManager, MenuBarManager, ToolBarManager
from traits.api import Bool, Callable, Enum, HasTraits, Instance, \
- List, Str, Trait, Tuple, Unicode
+ List, Property, Str, Trait, Tuple, Unicode
# Trait definitions.
SubSchema = Trait(None, Action, ActionItem, Group, MenuManager,
@@ -18,6 +18,9 @@ class Schema(HasTraits):
# The schema's identifier (unique within its parent schema).
id = Str
+ def _id_default(self):
+ return self.__class__.__name__ + get_unique_number()
+
# The list of sub-items in the schema. These items can be other
# (non-top-level) schema or concrete instances from the Pyface API.
items = List(SubSchema)
View
39 pyface/tasks/tests/test_action_manager_builder.py
@@ -167,6 +167,45 @@ def test_merging_redundant_items(self):
)
self.assertActionElementsEqual(actual, desired)
+ def test_unwanted_merge(self):
+ """ Test that we don't have automatic merges due to forgetting to set
+ a schema ID. """
+
+ # Initial menu.
+ schema = MenuBarSchema(
+ MenuSchema(GroupSchema(self.action1, id='FileGroup'),
+ name='File 1')
+ )
+
+ # Contributed menus.
+ extra_menu = MenuSchema(
+ GroupSchema(self.action2, id='FileGroup'),
+ name='File 2'
+ )
+
+ extra_actions = [
+ SchemaAddition(path='MenuBar',
+ factory=lambda : extra_menu,
+ id='DummyActionsSMenu'),
+ ]
+
+ # Build the final menu.
+ builder = TaskActionManagerBuilder(
+ task=Task(menu_bar=schema, extra_actions=extra_actions)
+ )
+ actual = builder.create_menu_bar_manager()
+
+ # Note that we expect the name of the menu to be inherited from
+ # the menu in the menu bar schema that is defined first.
+ desired = MenuBarManager(
+ MenuManager(Group(self.action1, id='FileGroup'),
+ name='File 1', id='MenuSchema_1'),
+ MenuManager(Group(self.action2, id='FileGroup'),
+ name='File 2', id='MenuSchema_2'),
+ id='MenuBar'
+ )
+ self.assertActionElementsEqual(actual, desired)
+
def test_merging_items_with_same_id_but_different_class(self):
""" Schemas with the same path but different types (menus, groups)
are not merged together.
View
66 pyface/util/id_helper.py
@@ -0,0 +1,66 @@
+#------------------------------------------------------------------------------
+# Copyright (c) 2005-2013, Enthought, Inc.
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in enthought/LICENSE.txt and may be redistributed only
+# under the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+# Thanks for using Enthought open source!
+#
+# Author: Enthought, Inc.
+# Description: <Enthought pyface package component>
+#------------------------------------------------------------------------------
+""" Helper functions to automatically generate unique IDs. """
+
+
+from weakref import WeakKeyDictionary
+
+class _ObjectCounter(object):
+ """ Counts objects. """
+
+ def __init__(self):
+ self._objects_registry = WeakKeyDictionary()
+
+ def get_count(self, obj):
+ """ Return the number of times an object was seen.
+
+ Objects must be hashable.
+
+ """
+
+ if obj in self._objects_registry:
+ count = self._objects_registry[obj]
+ else:
+ count = 0
+
+ return count
+
+ def next_count(self, obj):
+ """ Increase and return the number of times an object was seen.
+
+ Objects must be hashable.
+
+ """
+
+ count = self.get_count(obj)
+ self._objects_registry[obj] = count + 1
+ return self._objects_registry[obj]
+
+# Global object counter.
+object_counter = _ObjectCounter()
+
+
+def get_unique_id(object):
+ """ Return a unique ID of the form ClassName_X, where X is an integer.
+
+ It is only guaranteed that IDs are unique to a specific Python session, not
+ across sessions.
+
+ """
+
+ class_ = object.__class__
+ name = class_.__name__
+ number = object_counter.next_count(class_)
+
+ return name + '_' + str(number)
View
0 pyface/util/tests/__init__.py
No changes.
View
54 pyface/util/tests/test_id_helper.py
@@ -0,0 +1,54 @@
+#
+# Enthought product code
+#
+# (C) Copyright 2012 Enthought, Inc., Austin, TX
+# All right reserved.
+#
+# This file is confidential and NOT open source. Do not distribute.
+#
+""" Test the scripting tools. """
+
+
+import unittest
+from pyface.util.id_helper import get_unique_id, object_counter
+
+
+class IDHelperTestCase(unittest.TestCase):
+ """ Test the scripting tools. """
+
+ #### Tests ################################################################
+
+ def test_object_counter(self):
+
+ from traits.api import WeakRef
+
+ class Bogus(object):
+ weak = WeakRef
+
+ class Foo(object):
+ foo = 3
+
+ foo = Foo()
+
+ self.assertEqual(object_counter.get_count(Bogus), 0)
+ self.assertEqual(object_counter.next_count(Bogus), 1)
+ self.assertEqual(object_counter.next_count(Bogus), 2)
+ self.assertEqual(object_counter.get_count(Bogus), 2)
+ self.assertEqual(object_counter.next_count(foo), 1)
+ self.assertEqual(object_counter.next_count(Bogus), 3)
+
+ def test_get_unique_id(self):
+
+ class Bogus(object):
+ pass
+
+ bogus_1 = Bogus()
+ bogus_2 = Bogus()
+
+ self.assertEqual(get_unique_id(bogus_1), 'Bogus_1')
+ self.assertEqual(get_unique_id(bogus_2), 'Bogus_2')
+
+if __name__ == '__main__':
+ unittest.main()
+
+#### EOF ######################################################################

0 comments on commit 2daea8a

Please sign in to comment.
Something went wrong with that request. Please try again.