~ubuntu-branches/ubuntu/utopic/python-apptools/utopic

« back to all changes in this revision

Viewing changes to apptools/naming/context.py

  • Committer: Bazaar Package Importer
  • Author(s): Varun Hiremath
  • Date: 2011-07-08 23:55:50 UTC
  • mfrom: (2.1.9 sid)
  • Revision ID: james.westby@ubuntu.com-20110708235550-yz5u79ubeo4dhyfx
Tags: 4.0.0-1
* New upstream release
* Update debian/watch file

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#------------------------------------------------------------------------------
 
2
# Copyright (c) 2005, Enthought, Inc.
 
3
# All rights reserved.
 
4
#
 
5
# This software is provided without warranty under the terms of the BSD
 
6
# license included in enthought/LICENSE.txt and may be redistributed only
 
7
# under the conditions described in the aforementioned license.  The license
 
8
# is also available online at http://www.enthought.com/licenses/BSD.txt
 
9
# Thanks for using Enthought open source!
 
10
#
 
11
# Author: Enthought, Inc.
 
12
# Description: <Enthought naming package component>
 
13
#------------------------------------------------------------------------------
 
14
""" The base class for all naming contexts. """
 
15
 
 
16
 
 
17
# Enthought library imports.
 
18
from traits.api import Any, Dict, Event, HasTraits, Instance
 
19
from traits.api import Property, Str
 
20
from apptools.type_manager.api import TypeManager
 
21
 
 
22
# Local imports.
 
23
from binding import Binding
 
24
from exception import InvalidNameError, NameAlreadyBoundError
 
25
from exception import NameNotFoundError, NotContextError
 
26
from exception import OperationNotSupportedError
 
27
from naming_event import NamingEvent
 
28
from naming_manager import naming_manager
 
29
from object_factory import ObjectFactory
 
30
from state_factory import StateFactory
 
31
from unique_name import make_unique_name
 
32
 
 
33
 
 
34
# Constants for environment property keys.
 
35
INITIAL_CONTEXT_FACTORY = "apptools.naming.factory.initial"
 
36
OBJECT_FACTORIES = "apptools.naming.factory.object"
 
37
STATE_FACTORIES = "apptools.naming.factory.state"
 
38
 
 
39
# Non-JNDI.
 
40
TYPE_MANAGER = "apptools.naming.factory.type.manager"
 
41
 
 
42
 
 
43
# The default environment.
 
44
ENVIRONMENT = {
 
45
    # 'Context' properties.
 
46
    OBJECT_FACTORIES          : [],
 
47
    STATE_FACTORIES           : [],
 
48
 
 
49
    # Non-JNDI.
 
50
    TYPE_MANAGER              : None,
 
51
}
 
52
 
 
53
 
 
54
class Context(HasTraits):
 
55
    """ The base class for all naming contexts. """
 
56
 
 
57
    # Keys for environment properties.
 
58
    INITIAL_CONTEXT_FACTORY = INITIAL_CONTEXT_FACTORY
 
59
    OBJECT_FACTORIES = OBJECT_FACTORIES
 
60
    STATE_FACTORIES = STATE_FACTORIES
 
61
 
 
62
    # Non-JNDI.
 
63
    TYPE_MANAGER = TYPE_MANAGER
 
64
 
 
65
    #### 'Context' interface ##################################################
 
66
 
 
67
    # The naming environment in effect for this context.
 
68
    environment = Dict(ENVIRONMENT)
 
69
 
 
70
    # The name of the context within its own namespace.
 
71
    namespace_name = Property(Str)
 
72
 
 
73
    # The type manager in the context's environment (used to create context
 
74
    # adapters etc.).
 
75
    #
 
76
    # fixme: This is an experimental 'convenience' trait, since it is common
 
77
    # to get hold of the context's type manager to see if some object has a
 
78
    # context adapter.
 
79
    type_manager = Property(Instance(TypeManager))
 
80
 
 
81
    #### Events ####
 
82
 
 
83
    # Fired when an object has been added to the context (either via 'bind' or
 
84
    # 'create_subcontext').
 
85
    object_added = Event(NamingEvent)
 
86
 
 
87
    # Fired when an object has been changed (via 'rebind').
 
88
    object_changed = Event(NamingEvent)
 
89
 
 
90
    # Fired when an object has been removed from the context (either via
 
91
    # 'unbind' or 'destroy_subcontext').
 
92
    object_removed = Event(NamingEvent)
 
93
 
 
94
    # Fired when an object in the context has been renamed (via 'rename').
 
95
    object_renamed = Event(NamingEvent)
 
96
 
 
97
    # Fired when the contents of the context have changed dramatically.
 
98
    context_changed = Event(NamingEvent)
 
99
 
 
100
     #### Protected 'Context' interface #######################################
 
101
 
 
102
    # The bindings in the context.
 
103
    _bindings = Dict(Str, Any)
 
104
 
 
105
    ###########################################################################
 
106
    # 'Context' interface.
 
107
    ###########################################################################
 
108
 
 
109
    #### Properties ###########################################################
 
110
 
 
111
    def _get_namespace_name(self):
 
112
        """
 
113
        Return the name of the context within its own namespace.
 
114
 
 
115
        That is the full-path, through the namespace this context participates
 
116
        in, to get to this context.  For example, if the root context of the
 
117
        namespace was called 'Foo', and there was a subcontext of that called
 
118
        'Bar', and we were within that and called 'Baz', then this should
 
119
        return 'Foo/Bar/Baz'.
 
120
 
 
121
        """
 
122
 
 
123
        # FIXME: We'd like to raise an exception and force implementors to
 
124
        # decide what to do.  However, it appears to be pretty common that
 
125
        # most Context implementations do not override this method -- possibly
 
126
        # because the comments aren't clear on what this is supposed to be?
 
127
        #
 
128
        # Anyway, if we raise an exception then it is impossible to use any
 
129
        # evaluations when building a Traits UI for a Context.  That is, the
 
130
        # Traits UI can't include items that have a 'visible_when' or
 
131
        # 'enabled_when' evaluation.  This is because the Traits evaluation
 
132
        # code calls the 'get()' method on the Context which attempts to
 
133
        # retrieve the current namespace_name value.
 
134
        #raise OperationNotSupportedError()
 
135
        return ''
 
136
 
 
137
    def _get_type_manager(self):
 
138
        """ Returns the type manager in the context's environment.
 
139
 
 
140
        This will return None if no type manager was used to create the initial
 
141
        context.
 
142
 
 
143
        """
 
144
 
 
145
        return self.environment.get(self.TYPE_MANAGER)
 
146
 
 
147
    #### Methods ##############################################################
 
148
 
 
149
    def bind(self, name, obj, make_contexts=False):
 
150
        """ Binds a name to an object.
 
151
 
 
152
        If 'make_contexts' is True then any missing intermediate contexts are
 
153
        created automatically.
 
154
 
 
155
        """
 
156
 
 
157
        if len(name) == 0:
 
158
            raise InvalidNameError('empty name')
 
159
 
 
160
        # Parse the name.
 
161
        components = self._parse_name(name)
 
162
 
 
163
        # If there is exactly one component in the name then the operation
 
164
        # takes place in this context.
 
165
        if len(components) == 1:
 
166
            atom = components[0]
 
167
 
 
168
            # Is the name already bound?
 
169
            if self._is_bound(atom):
 
170
                raise NameAlreadyBoundError(name)
 
171
 
 
172
            # Do the actual bind.
 
173
            self._bind(atom, obj)
 
174
 
 
175
            # Trait event notification.
 
176
            self.object_added = NamingEvent(
 
177
                new_binding=Binding(name=name, obj=obj, context=self)
 
178
            )
 
179
 
 
180
        # Otherwise, attempt to continue resolution into the next context.
 
181
        else:
 
182
            if not self._is_bound(components[0]):
 
183
                if make_contexts:
 
184
                    self._create_subcontext(components[0])
 
185
 
 
186
                else:
 
187
                    raise NameNotFoundError(components[0])
 
188
 
 
189
            next_context = self._get_next_context(components[0])
 
190
            next_context.bind('/'.join(components[1:]), obj, make_contexts)
 
191
 
 
192
        return
 
193
 
 
194
    def rebind(self, name, obj, make_contexts=False):
 
195
        """ Binds an object to a name that may already be bound.
 
196
 
 
197
        If 'make_contexts' is True then any missing intermediate contexts are
 
198
        created automatically.
 
199
 
 
200
        The object may be a different object but may also be the same object
 
201
        that is already bound to the specified name. The name may or may not be
 
202
        already used. Think of this as a safer version of 'bind' since this
 
203
        one will never raise an exception regarding a name being used.
 
204
 
 
205
        """
 
206
 
 
207
        if len(name) == 0:
 
208
            raise InvalidNameError('empty name')
 
209
 
 
210
        # Parse the name.
 
211
        components = self._parse_name(name)
 
212
 
 
213
        # If there is exactly one component in the name then the operation
 
214
        # takes place in this context.
 
215
        if len(components) == 1:
 
216
            # Do the actual rebind.
 
217
            self._rebind(components[0], obj)
 
218
 
 
219
            # Trait event notification.
 
220
            self.object_changed = NamingEvent(
 
221
                new_binding=Binding(name=name, obj=obj, context=self)
 
222
            )
 
223
 
 
224
        # Otherwise, attempt to continue resolution into the next context.
 
225
        else:
 
226
            if not self._is_bound(components[0]):
 
227
                if make_contexts:
 
228
                    self._create_subcontext(components[0])
 
229
 
 
230
                else:
 
231
                    raise NameNotFoundError(components[0])
 
232
 
 
233
            next_context = self._get_next_context(components[0])
 
234
            next_context.rebind('/'.join(components[1:]), obj, make_contexts)
 
235
 
 
236
        return
 
237
 
 
238
    def unbind(self, name):
 
239
        """ Unbinds a name. """
 
240
 
 
241
        if len(name) == 0:
 
242
            raise InvalidNameError('empty name')
 
243
 
 
244
        # Parse the name.
 
245
        components = self._parse_name(name)
 
246
 
 
247
        # If there is exactly one component in the name then the operation
 
248
        # takes place in this context.
 
249
        if len(components) == 1:
 
250
            atom = components[0]
 
251
 
 
252
            if not self._is_bound(atom):
 
253
                raise NameNotFoundError(name)
 
254
 
 
255
            # Lookup the object that we are unbinding to use in the event
 
256
            # notification.
 
257
            obj = self._lookup(atom)
 
258
 
 
259
            # Do the actual unbind.
 
260
            self._unbind(atom)
 
261
 
 
262
            # Trait event notification.
 
263
            self.object_removed = NamingEvent(
 
264
                old_binding=Binding(name=name, obj=obj, context=self)
 
265
            )
 
266
 
 
267
        # Otherwise, attempt to continue resolution into the next context.
 
268
        else:
 
269
            if not self._is_bound(components[0]):
 
270
                raise NameNotFoundError(components[0])
 
271
 
 
272
            next_context = self._get_next_context(components[0])
 
273
            next_context.unbind('/'.join(components[1:]))
 
274
 
 
275
        return
 
276
 
 
277
    def rename(self, old_name, new_name):
 
278
        """ Binds a new name to an object. """
 
279
 
 
280
        if len(old_name) == 0 or len(new_name) == 0:
 
281
            raise InvalidNameError('empty name')
 
282
 
 
283
        # Parse the names.
 
284
        old_components = self._parse_name(old_name)
 
285
        new_components = self._parse_name(new_name)
 
286
 
 
287
        # If there is axactly one component in BOTH names then the operation
 
288
        # takes place ENTIRELY in this context.
 
289
        if len(old_components) == 1 and len(new_components) == 1:
 
290
            # Is the old name actually bound?
 
291
            if not self._is_bound(old_name):
 
292
                raise NameNotFoundError(old_name)
 
293
 
 
294
            # Is the new name already bound?
 
295
            if self._is_bound(new_name):
 
296
                raise NameAlreadyBoundError(new_name)
 
297
 
 
298
            # Do the actual rename.
 
299
            self._rename(old_name, new_name)
 
300
 
 
301
            # Lookup the object that we are renaming to use in the event
 
302
            # notification.
 
303
            obj = self._lookup(new_name)
 
304
 
 
305
            # Trait event notification.
 
306
            self.object_renamed = NamingEvent(
 
307
                old_binding=Binding(name=old_name, obj=obj, context=self),
 
308
                new_binding=Binding(name=new_name, obj=obj, context=self)
 
309
            )
 
310
 
 
311
        else:
 
312
            # fixme: This really needs to be transactional in case the bind
 
313
            # succeeds but the unbind fails.  To be safe should we just not
 
314
            # support cross-context renaming for now?!?!
 
315
            #
 
316
            # Lookup the object.
 
317
            obj = self.lookup(old_name)
 
318
 
 
319
            # Bind the new name.
 
320
            self.bind(new_name, obj)
 
321
 
 
322
            # Unbind the old one.
 
323
            self.unbind(old_name)
 
324
 
 
325
        return
 
326
 
 
327
    def lookup(self, name):
 
328
        """ Resolves a name relative to this context. """
 
329
 
 
330
        # If the name is empty we return the context itself.
 
331
        if len(name) == 0:
 
332
            # fixme: The JNDI spec. says that this should return a COPY of
 
333
            # the context.
 
334
            return self
 
335
 
 
336
        # Parse the name.
 
337
        components = self._parse_name(name)
 
338
 
 
339
        # If there is exactly one component in the name then the operation
 
340
        # takes place in this context.
 
341
        if len(components) == 1:
 
342
            atom = components[0]
 
343
 
 
344
            if not self._is_bound(atom):
 
345
                raise NameNotFoundError(name)
 
346
 
 
347
            # Do the actual lookup.
 
348
            obj = self._lookup(atom)
 
349
 
 
350
        # Otherwise, attempt to continue resolution into the next context.
 
351
        else:
 
352
            if not self._is_bound(components[0]):
 
353
                raise NameNotFoundError(components[0])
 
354
 
 
355
            next_context = self._get_next_context(components[0])
 
356
            obj = next_context.lookup('/'.join(components[1:]))
 
357
 
 
358
        return obj
 
359
 
 
360
 
 
361
    # fixme: Non-JNDI
 
362
    def lookup_binding(self, name):
 
363
        """ Looks up the binding for a name relative to this context. """
 
364
 
 
365
        if len(name) == 0:
 
366
            raise InvalidNameError('empty name')
 
367
 
 
368
        # Parse the name.
 
369
        components = self._parse_name(name)
 
370
 
 
371
        # If there is exactly one component in the name then the operation
 
372
        # takes place in this context.
 
373
        if len(components) == 1:
 
374
            atom = components[0]
 
375
 
 
376
            if not self._is_bound(atom):
 
377
                raise NameNotFoundError(name)
 
378
 
 
379
            # Do the actual lookup.
 
380
            binding = self._lookup_binding(atom)
 
381
 
 
382
        # Otherwise, attempt to continue resolution into the next context.
 
383
        else:
 
384
            if not self._is_bound(components[0]):
 
385
                raise NameNotFoundError(components[0])
 
386
 
 
387
            next_context = self._get_next_context(components[0])
 
388
            binding = next_context.lookup_binding('/'.join(components[1:]))
 
389
 
 
390
        return binding
 
391
 
 
392
    # fixme: Non-JNDI
 
393
    def lookup_context(self, name):
 
394
        """ Resolves a name relative to this context.
 
395
 
 
396
        The name MUST resolve to a context. This method is useful to return
 
397
        context adapters.
 
398
 
 
399
        """
 
400
 
 
401
        # If the name is empty we return the context itself.
 
402
        if len(name) == 0:
 
403
            # fixme: The JNDI spec. says that this should return a COPY of
 
404
            # the context.
 
405
            return self
 
406
 
 
407
        # Parse the name.
 
408
        components = self._parse_name(name)
 
409
 
 
410
        # If there is exactly one component in the name then the operation
 
411
        # takes place in this context.
 
412
        if len(components) == 1:
 
413
            atom = components[0]
 
414
 
 
415
            if not self._is_bound(atom):
 
416
                raise NameNotFoundError(name)
 
417
 
 
418
            # Do the actual lookup.
 
419
            obj = self._get_next_context(atom)
 
420
 
 
421
        # Otherwise, attempt to continue resolution into the next context.
 
422
        else:
 
423
            if not self._is_bound(components[0]):
 
424
                raise NameNotFoundError(components[0])
 
425
 
 
426
            next_context = self._get_next_context(components[0])
 
427
            obj = next_context.lookup('/'.join(components[1:]))
 
428
 
 
429
        return obj
 
430
 
 
431
    def create_subcontext(self, name):
 
432
        """ Creates a sub-context. """
 
433
 
 
434
        if len(name) == 0:
 
435
            raise InvalidNameError('empty name')
 
436
 
 
437
        # Parse the name.
 
438
        components = self._parse_name(name)
 
439
 
 
440
        # If there is exactly one component in the name then the operation
 
441
        # takes place in this context.
 
442
        if len(components) == 1:
 
443
            atom = components[0]
 
444
 
 
445
            # Is the name already bound?
 
446
            if self._is_bound(atom):
 
447
                raise NameAlreadyBoundError(name)
 
448
 
 
449
            # Do the actual creation of the sub-context.
 
450
            sub = self._create_subcontext(atom)
 
451
 
 
452
            # Trait event notification.
 
453
            self.object_added = NamingEvent(
 
454
                new_binding=Binding(name=name, obj=sub, context=self)
 
455
            )
 
456
 
 
457
        # Otherwise, attempt to continue resolution into the next context.
 
458
        else:
 
459
            if not self._is_bound(components[0]):
 
460
                raise NameNotFoundError(components[0])
 
461
 
 
462
            next_context = self._get_next_context(components[0])
 
463
            sub = next_context.create_subcontext('/'.join(components[1:]))
 
464
 
 
465
        return sub
 
466
 
 
467
    def destroy_subcontext(self, name):
 
468
        """ Destroys a sub-context. """
 
469
 
 
470
        if len(name) == 0:
 
471
            raise InvalidNameError('empty name')
 
472
 
 
473
        # Parse the name.
 
474
        components = self._parse_name(name)
 
475
 
 
476
        # If there is exactly one component in the name then the operation
 
477
        # takes place in this context.
 
478
        if len(components) == 1:
 
479
            atom = components[0]
 
480
 
 
481
            if not self._is_bound(atom):
 
482
                raise NameNotFoundError(name)
 
483
 
 
484
            obj = self._lookup(atom)
 
485
            if not self._is_context(atom):
 
486
               raise NotContextError(name)
 
487
 
 
488
            # Do the actual destruction of the sub-context.
 
489
            self._destroy_subcontext(atom)
 
490
 
 
491
            # Trait event notification.
 
492
            self.object_removed = NamingEvent(
 
493
                old_binding=Binding(name=name, obj=obj, context=self)
 
494
            )
 
495
 
 
496
        # Otherwise, attempt to continue resolution into the next context.
 
497
        else:
 
498
            if not self._is_bound(components[0]):
 
499
                raise NameNotFoundError(components[0])
 
500
 
 
501
            next_context = self._get_next_context(components[0])
 
502
            next_context.destroy_subcontext('/'.join(components[1:]))
 
503
 
 
504
        return
 
505
 
 
506
    # fixme: Non-JNDI
 
507
    def get_unique_name(self, prefix):
 
508
        """ Returns a name that is unique within the context.
 
509
 
 
510
        The name returned will start with the specified prefix.
 
511
 
 
512
        """
 
513
 
 
514
        return make_unique_name(prefix, existing=self.list_names(''),
 
515
            format='%s (%d)')
 
516
 
 
517
 
 
518
    def list_names(self, name=''):
 
519
        """ Lists the names bound in a context. """
 
520
 
 
521
        # If the name is empty then the operation takes place in this context.
 
522
        if len(name) == 0:
 
523
            names = self._list_names()
 
524
 
 
525
        # Otherwise, attempt to continue resolution into the next context.
 
526
        else:
 
527
            # Parse the name.
 
528
            components = self._parse_name(name)
 
529
 
 
530
            if not self._is_bound(components[0]):
 
531
                raise NameNotFoundError(components[0])
 
532
 
 
533
            next_context = self._get_next_context(components[0])
 
534
            names = next_context.list_names('/'.join(components[1:]))
 
535
 
 
536
        return names
 
537
 
 
538
    def list_bindings(self, name=''):
 
539
        """ Lists the bindings in a context. """
 
540
 
 
541
        # If the name is empty then the operation takes place in this context.
 
542
        if len(name) == 0:
 
543
            bindings = self._list_bindings()
 
544
 
 
545
        # Otherwise, attempt to continue resolution into the next context.
 
546
        else:
 
547
            # Parse the name.
 
548
            components = self._parse_name(name)
 
549
 
 
550
            if not self._is_bound(components[0]):
 
551
                raise NameNotFoundError(components[0])
 
552
 
 
553
            next_context = self._get_next_context(components[0])
 
554
            bindings = next_context.list_bindings('/'.join(components[1:]))
 
555
 
 
556
        return bindings
 
557
 
 
558
    # fixme: Non-JNDI
 
559
    def is_context(self, name):
 
560
        """ Returns True if the name is bound to a context. """
 
561
 
 
562
        # If the name is empty then it refers to this context.
 
563
        if len(name) == 0:
 
564
            is_context = True
 
565
 
 
566
        else:
 
567
            # Parse the name.
 
568
            components = self._parse_name(name)
 
569
 
 
570
            # If there is exactly one component in the name then the operation
 
571
            # takes place in this context.
 
572
            if len(components) == 1:
 
573
                atom = components[0]
 
574
 
 
575
                if not self._is_bound(atom):
 
576
                    raise NameNotFoundError(name)
 
577
 
 
578
                # Do the actual check.
 
579
                is_context = self._is_context(atom)
 
580
 
 
581
            # Otherwise, attempt to continue resolution into the next context.
 
582
            else:
 
583
                if not self._is_bound(components[0]):
 
584
                    raise NameNotFoundError(components[0])
 
585
 
 
586
                next_context = self._get_next_context(components[0])
 
587
                is_context = next_context.is_context('/'.join(components[1:]))
 
588
 
 
589
        return is_context
 
590
 
 
591
    # fixme: Non-JNDI
 
592
    def search(self, obj):
 
593
        """ Returns a list of namespace names that are bound to obj. """
 
594
 
 
595
        # don't look for None
 
596
        if obj is None:
 
597
            return []
 
598
 
 
599
        # Obj is bound to these names relative to this context
 
600
        names = []
 
601
 
 
602
        # path contain the name components down to the current context
 
603
        path = []
 
604
 
 
605
        self._search( obj, names, path, {} )
 
606
 
 
607
        return names
 
608
 
 
609
    ###########################################################################
 
610
    # Protected 'Context' interface.
 
611
    ###########################################################################
 
612
 
 
613
    def _parse_name(self, name):
 
614
        """ Parse a name into a list of components.
 
615
 
 
616
        e.g. 'foo/bar/baz' -> ['foo', 'bar', 'baz']
 
617
 
 
618
        """
 
619
 
 
620
        return name.split('/')
 
621
 
 
622
    def _is_bound(self, name):
 
623
        """ Is a name bound in this context? """
 
624
 
 
625
        return name in self._bindings
 
626
 
 
627
    def _lookup(self, name):
 
628
        """ Looks up a name in this context. """
 
629
 
 
630
        obj = self._bindings[name]
 
631
 
 
632
        return naming_manager.get_object_instance(obj, name, self)
 
633
 
 
634
    def _lookup_binding(self, name):
 
635
        """ Looks up the binding for a name in this context. """
 
636
 
 
637
        return Binding(name=name, obj=self._lookup(name), context=self)
 
638
 
 
639
    def _bind(self, name, obj):
 
640
        """ Binds a name to an object in this context. """
 
641
 
 
642
        state = naming_manager.get_state_to_bind(obj, name, self)
 
643
        self._bindings[name] = state
 
644
 
 
645
        return
 
646
 
 
647
    def _rebind(self, name, obj):
 
648
        """ Rebinds a name to an object in this context. """
 
649
 
 
650
        self._bind(name, obj)
 
651
 
 
652
        return
 
653
 
 
654
    def _unbind(self, name):
 
655
        """ Unbinds a name from this context. """
 
656
 
 
657
        del self._bindings[name]
 
658
 
 
659
        return
 
660
 
 
661
    def _rename(self, old_name, new_name):
 
662
        """ Renames an object in this context. """
 
663
 
 
664
        # Bind the new name.
 
665
        self._bindings[new_name] = self._bindings[old_name]
 
666
 
 
667
        # Unbind the old one.
 
668
        del self._bindings[old_name]
 
669
 
 
670
        return
 
671
 
 
672
    def _create_subcontext(self, name):
 
673
        """ Creates a sub-context of this context. """
 
674
 
 
675
        sub = self.__class__(environment=self.environment)
 
676
        self._bindings[name] = sub
 
677
 
 
678
        return sub
 
679
 
 
680
    def _destroy_subcontext(self, name):
 
681
        """ Destroys a sub-context of this context. """
 
682
 
 
683
        del self._bindings[name]
 
684
 
 
685
        return
 
686
 
 
687
    def _list_bindings(self):
 
688
        """ Lists the bindings in this context. """
 
689
 
 
690
        bindings = []
 
691
        for name in self._list_names():
 
692
            bindings.append(
 
693
                Binding(name=name, obj=self._lookup(name), context=self)
 
694
            )
 
695
 
 
696
        return bindings
 
697
 
 
698
    def _list_names(self):
 
699
        """ Lists the names bound in this context. """
 
700
 
 
701
        return self._bindings.keys()
 
702
 
 
703
    def _is_context(self, name):
 
704
        """ Returns True if a name is bound to a context. """
 
705
 
 
706
        return self._get_next_context(name) is not None
 
707
 
 
708
    def _get_next_context(self, name):
 
709
        """ Returns the next context. """
 
710
 
 
711
        obj = self._lookup(name)
 
712
 
 
713
        # If the object is a context then everything is just dandy.
 
714
        if isinstance(obj, Context):
 
715
            next_context = obj
 
716
 
 
717
        # Otherwise, instead of just giving up, see if the context has a type
 
718
        # manager that knows how to adapt the object to make it quack like a
 
719
        # context.
 
720
        else:
 
721
            next_context = self._get_context_adapter(obj)
 
722
 
 
723
            # If no adapter was found then we cannot continue name resolution.
 
724
            if next_context is None:
 
725
                raise NotContextError(name)
 
726
 
 
727
        return next_context
 
728
 
 
729
    def _search( self, obj, names, path, searched):
 
730
        """ Append to names any name bound to obj.
 
731
            Join path and name with '/' to for a complete name from the
 
732
            top context.
 
733
        """
 
734
 
 
735
        # Check the bindings recursively.
 
736
        for binding in self.list_bindings():
 
737
            if binding.obj is obj:
 
738
                path.append( binding.name )
 
739
                names.append( '/'.join(path) )
 
740
                path.pop()
 
741
 
 
742
            if isinstance( binding.obj, Context ) \
 
743
                and not binding.obj in searched:
 
744
                path.append( binding.name )
 
745
                searched[binding.obj] = True
 
746
                binding.obj._search( obj, names, path, searched )
 
747
                path.pop()
 
748
 
 
749
        return
 
750
 
 
751
    ###########################################################################
 
752
    # Private interface.
 
753
    ###########################################################################
 
754
 
 
755
    def _get_context_adapter(self, obj):
 
756
        """ Returns a context adapter for an object.
 
757
 
 
758
        Returns None if no such adapter is available.
 
759
 
 
760
        """
 
761
 
 
762
        if self.type_manager is not None:
 
763
            adapter = self.type_manager.object_as(
 
764
                obj, Context, environment=self.environment, context=self
 
765
            )
 
766
 
 
767
        else:
 
768
            adapter = None
 
769
 
 
770
        return adapter
 
771
 
 
772
#### EOF ######################################################################