Show More
@@ -37,6 +37,9 b' class ConfigurableError(Exception):' | |||||
37 | pass |
|
37 | pass | |
38 |
|
38 | |||
39 |
|
39 | |||
|
40 | class MultipleInstanceError(ConfigurableError): | |||
|
41 | pass | |||
|
42 | ||||
40 | #----------------------------------------------------------------------------- |
|
43 | #----------------------------------------------------------------------------- | |
41 | # Configurable implementation |
|
44 | # Configurable implementation | |
42 | #----------------------------------------------------------------------------- |
|
45 | #----------------------------------------------------------------------------- | |
@@ -170,4 +173,62 b' class Configurable(HasTraits):' | |||||
170 | def class_print_help(cls): |
|
173 | def class_print_help(cls): | |
171 | print cls.class_get_help() |
|
174 | print cls.class_get_help() | |
172 |
|
175 | |||
173 | No newline at end of file |
|
176 | ||
|
177 | class SingletonConfigurable(Configurable): | |||
|
178 | """A configurable that only allows one instance. | |||
|
179 | ||||
|
180 | This class is for classes that should only have one instance of itself | |||
|
181 | or *any* subclass. To create and retrieve such a class use the | |||
|
182 | :meth:`SingletonConfigurable.instance` method. | |||
|
183 | """ | |||
|
184 | ||||
|
185 | _instance = None | |||
|
186 | ||||
|
187 | @classmethod | |||
|
188 | def instance(cls, *args, **kwargs): | |||
|
189 | """Returns a global instance of this class. | |||
|
190 | ||||
|
191 | This method create a new instance if none have previously been created | |||
|
192 | and returns a previously created instance is one already exists. | |||
|
193 | ||||
|
194 | The arguments and keyword arguments passed to this method are passed | |||
|
195 | on to the :meth:`__init__` method of the class upon instantiation. | |||
|
196 | ||||
|
197 | Examples | |||
|
198 | -------- | |||
|
199 | ||||
|
200 | Create a singleton class using instance, and retrieve it:: | |||
|
201 | ||||
|
202 | >>> from IPython.config.configurable import SingletonConfigurable | |||
|
203 | >>> class Foo(SingletonConfigurable): pass | |||
|
204 | >>> foo = Foo.instance() | |||
|
205 | >>> foo == Foo.instance() | |||
|
206 | True | |||
|
207 | ||||
|
208 | Create a subclass that is retrived using the base class instance:: | |||
|
209 | ||||
|
210 | >>> class Bar(SingletonConfigurable): pass | |||
|
211 | >>> class Bam(Bar): pass | |||
|
212 | >>> bam = Bam.instance() | |||
|
213 | >>> bam == Bar.instance() | |||
|
214 | True | |||
|
215 | """ | |||
|
216 | # Create and save the instance | |||
|
217 | if cls._instance is None: | |||
|
218 | inst = cls(*args, **kwargs) | |||
|
219 | # Now make sure that the instance will also be returned by | |||
|
220 | # the subclasses instance attribute. | |||
|
221 | for subclass in cls.mro(): | |||
|
222 | if issubclass(cls, subclass) and \ | |||
|
223 | issubclass(subclass, SingletonConfigurable) and \ | |||
|
224 | subclass != SingletonConfigurable: | |||
|
225 | subclass._instance = inst | |||
|
226 | else: | |||
|
227 | break | |||
|
228 | if isinstance(cls._instance, cls): | |||
|
229 | return cls._instance | |||
|
230 | else: | |||
|
231 | raise MultipleInstanceError( | |||
|
232 | 'Multiple incompatible subclass instances of ' | |||
|
233 | '%s are being created.' % cls.__name__ | |||
|
234 | ) |
@@ -22,10 +22,15 b' Authors:' | |||||
22 |
|
22 | |||
23 | from unittest import TestCase |
|
23 | from unittest import TestCase | |
24 |
|
24 | |||
25 |
from IPython.config.configurable import |
|
25 | from IPython.config.configurable import ( | |
|
26 | Configurable, | |||
|
27 | SingletonConfigurable | |||
|
28 | ) | |||
|
29 | ||||
26 | from IPython.utils.traitlets import ( |
|
30 | from IPython.utils.traitlets import ( | |
27 |
|
|
31 | Int, Float, Str | |
28 | ) |
|
32 | ) | |
|
33 | ||||
29 | from IPython.config.loader import Config |
|
34 | from IPython.config.loader import Config | |
30 |
|
35 | |||
31 |
|
36 | |||
@@ -57,7 +62,7 b' class Bar(Foo):' | |||||
57 | c = Float(config=True, shortname="c", help="The string c.") |
|
62 | c = Float(config=True, shortname="c", help="The string c.") | |
58 |
|
63 | |||
59 |
|
64 | |||
60 |
class TestConfigurable |
|
65 | class TestConfigurable(TestCase): | |
61 |
|
66 | |||
62 | def test_default(self): |
|
67 | def test_default(self): | |
63 | c1 = Configurable() |
|
68 | c1 = Configurable() | |
@@ -141,3 +146,23 b' class TestConfigurableConfig(TestCase):' | |||||
141 | def test_help(self): |
|
146 | def test_help(self): | |
142 | self.assertEquals(MyConfigurable.class_get_help(), mc_help) |
|
147 | self.assertEquals(MyConfigurable.class_get_help(), mc_help) | |
143 |
|
148 | |||
|
149 | ||||
|
150 | class TestSingletonConfigurable(TestCase): | |||
|
151 | ||||
|
152 | def test_instance(self): | |||
|
153 | from IPython.config.configurable import SingletonConfigurable | |||
|
154 | class Foo(SingletonConfigurable): pass | |||
|
155 | foo = Foo.instance() | |||
|
156 | self.assertEquals(foo, Foo.instance()) | |||
|
157 | self.assertEquals(SingletonConfigurable._instance, None) | |||
|
158 | ||||
|
159 | def test_inheritance(self): | |||
|
160 | ||||
|
161 | class Bar(SingletonConfigurable): pass | |||
|
162 | class Bam(Bar): pass | |||
|
163 | bam = Bam.instance() | |||
|
164 | bam == Bar.instance() | |||
|
165 | self.assertEquals(bam, Bam._instance) | |||
|
166 | self.assertEquals(bam, Bar._instance) | |||
|
167 | self.assertEquals(SingletonConfigurable._instance, None) | |||
|
168 | No newline at end of file |
@@ -117,7 +117,6 b' class TestKeyValueCL(TestCase):' | |||||
117 | def test_basic(self): |
|
117 | def test_basic(self): | |
118 | cl = KeyValueConfigLoader() |
|
118 | cl = KeyValueConfigLoader() | |
119 | argv = [s.strip('c.') for s in pyfile.split('\n')[2:-1]] |
|
119 | argv = [s.strip('c.') for s in pyfile.split('\n')[2:-1]] | |
120 | print argv |
|
|||
121 | config = cl.load_config(argv) |
|
120 | config = cl.load_config(argv) | |
122 | self.assertEquals(config.a, 10) |
|
121 | self.assertEquals(config.a, 10) | |
123 | self.assertEquals(config.b, 20) |
|
122 | self.assertEquals(config.b, 20) |
General Comments 0
You need to be logged in to leave comments.
Login now