File: Namespace.py

package info (click to toggle)
egenix-mx-base 2.0.6-1
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 3,028 kB
  • ctags: 4,762
  • sloc: ansic: 14,965; python: 11,739; sh: 313; makefile: 117
file content (444 lines) | stat: -rw-r--r-- 13,088 bytes parent folder | download | duplicates (3)
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
""" Namespace objects.

    Copyright (c) 1998-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com
    Copyright (c) 2000-2001, eGenix.com Software GmbH; mailto:info@egenix.com
    See the documentation for further information on copyrights,
    or contact the author. All Rights Reserved.
"""
import string,types
import mx.Tools.NewBuiltins
from mx.Tools import freeze

class Namespace:

    """ Base class for Namespaces.

        Instances of this namespace can store arbitrary attributes,
        which are stored in the instances dictionary. They are then
        available via attribute lookup, e.g. ns.attr, and index
        lookup, e.g. ns['attr'].

        The Namespaces support acquisition. To link the object to
        another object, set self.baseobj to the other
        object. Attributes not found in the Namespace instance are
        then looked up in the other object as well.

        NOTE: All methods and internal attributes must start with an
              underscore. Namespace attributes must *not* start with an
              underscore to prevent naming collisions with internal
              variables and methods.  Note that names with leading
              underscore cannot be acquired.

    """

    def __init__(self,**kws):

        """ Initializes the namespace with the attributes given
            as keywords.
        """
        if kws:
            self.__dict__.update(kws)
    
    def _clear(self,save=(),recursive=0,

               getattr=getattr):

        """ Clears the namespace.

            The attributes named in save are not cleared. Per default
            all attributes are cleared. You may want to save the
            baseobj attribute in case you use it.

            Class variables are not cleared this way, but overriding
            instance variables do get deleted.

            If recursive is true (default is false), the namespace is
            cleared recursively, meaning that all Namespace instances
            contained in it are also cleared.

        """
        d = {}
        for attr in save:
            d[attr] = getattr(self,attr)
        # Clear the old instance dictionary (recursively) so that it
        # can be garbage collected
        if recursive:
            for obj in self.__dict__.items():
                if isinstance(obj, Namespace):
                    obj._clear()
        obj = None
        self.__dict__.clear()
        # Assing the new one
        self.__dict__ = d

    def _update(self, other_namespace,

                DictType=types.DictType,type=type):

        """ Updates the namespace dictionary with the new entries
            taken from the object other_namespace.

            other_namespace can either be a dictionary or another
            Namespace object.

        """
        if type(other_namespace) is DictType:
            self.__dict__.update(other_namespace)
        else:
            self.__dict__.update(other_namespace.__dict__)

    def _load(self, other_namespace, save=(),
              ignore_types=(types.BuiltinMethodType,
                            types.ClassType,
                            types.MethodType,
                            types.FunctionType),

              DictType=types.DictType,type=type):

        """ Updates the namespace with the updated entries taken from
            the object other_namespace provided that the entries already
            exist in the namespace dictionary

            other_namespace can either be a dictionary or another
            Namespace object.

            Attributes listed in save are not overwritten. New values
            having one of the types listed in ignore_types are also
            ignored.

        """
        if type(other_namespace) is DictType:
            items = other_namespace.items()
        else:
            items = other_namespace._items()
        d = self.__dict__
        exists = d.has_key
        for k, v in items:
            if exists(k):
                if k in save or \
                   type(v) in ignore_types:
                    continue
                d[k] = v
        exists = None
        d = None

    def _dictionary(self):

        """ Returns the current namespace contents as dictionary.

            The dictionaries values should be considered read-only in
            case they are mutable.

        """
        return self.__dict__.copy()

    def _copy(self):

        """ Returns a shallow copy of the current namespace.

            See the standard Python module "copy" for details on the
            copying mechanism.

        """
        import copy
        return copy.copy(self)

    def _deepcopy(self):

        """ Returns a deep copy of the current namespace.

            See the standard Python module "copy" for details on the
            copying mechanism.

        """
        import copy
        return copy.deepcopy(self)

    ### Index interface

    def __getitem__(self,name,

                    getattr=getattr):

        """ Index interface to the namespace.

            Raises IndexErrors in case an attribute is not found.

        """
        try:
            return getattr(self, name)
        except AttributeError,why:
            raise IndexError,why
        
    def __setitem__(self,name,value,

                    setattr=setattr):

        """ Index interface to the namespace.
        """
        return setattr(self,name,value)

    def __len__(self):

        """ Return the number of instance attributes stored in the
            namespace.
        """
        return len(self.__dict__)

    def __nonzero__(self):

        """ Returns 1/0 depending on whether any instance attributes
            are defined in the namespace.

            An empty Namespace is false, if it contains at least one
            entry it is true.
            
        """
        return (len(self.__dict__) > 0)

    ### Dictionary like interface to the instance dictionary

    def _items(self):

        """ Returns a list of tuples (attrname, attrvalue) for
            all (instance) attributes stored in the object.
        """
        return self.__dict__.items()

    def _keys(self):

        """ Returns a list of attribute names for
            all (instance) attributes stored in the object.
        """
        return self.__dict__.keys()

    def _values(self):

        """ Returns a list of attribute values for
            all (instance) attributes stored in the object.
        """
        return self.__dict__.values()

    def _has_key(self, key):

        """ Returns 1/0 depending on whether the object has
            an instance attribute key or not.

            Note that class attributes cannot be queried this way.
            
        """
        return self.__dict__.has_key(key)

    # Alias
    _hasattr = _has_key

    def _get(self,key,default=None):

        """ Returns the value of attribute key, or default if no
            such attribute is defined.
        """
        return self.__dict__.get(key,default)

    # Alias
    _getattr = _get

    ### Additional methods:

    def _extract(self,names,defaults=(),

                 extract=extract):

        """ Extract a list of named objects from the namespace.
        
            defaults may be given as sequence of the same size as
            names to provide default values in case names are not
            found in the namespace.

            Only the instance dictionary is scanned, the class
            variables are not available through this mechanism.

        """
        return extract(self.__dict__,names,defaults)

    def _prefixgroup(self,prefix,nonzero=0,wholename=0):

        """ Find all attributes having prefix as common prefix and return
            a dictionary mapping the attribute suffixes (the prefix
            is removed) to their values.
            
            If nonzero is set, then only fields with non-zero value
            are included in the dictionary

            If wholename is true, the prefix is not removed from the
            attribute names.

        """
        plen = len(prefix)
        d = {}
        if not nonzero and not wholename:
            # Shortcut for the default settings
            for k,v in self.__dict__.items():
                if k[:plen] == prefix:
                    d[k[plen:]] = v
        else:
            for k,v in self.__dict__.items():
                if k[:plen] == prefix:
                    if nonzero and not v:
                        continue
                    if wholename:
                        d[k] = v
                    else:
                        d[k[plen:]] = v
        return d

    ### Acquistion:
        
    baseobj = None              # Base object ("containing" this object)

    # Implicit acquisition
    __getattr__ = acquire       # Use the (new) builtin acquire() to implement
                                # the acquisition mechanism

    ### For debugging purposes:

    def __repr__(self,

                 join=string.join,id=id):

        keys = self.__dict__.keys()
        keys.sort()
        return '<%s.%s (%s) at 0x%x>' % (
            self.__class__.__module__,
            self.__class__.__name__,
            join(keys,','),
            id(self))

    def __str__(self,

                join=string.join,id=id):

        items = self.__dict__.items()
        items.sort()
        l = ['%s.%s at 0x%x:' % (self.__class__.__module__,
                                 self.__class__.__name__,
                                 id(self))]
        for k,v in items:
            try:
                s = repr(v)
            except (ValueError, TypeError, AttributeError):
                s = '*** repr(value) failed ***'
            else:
                if len(s) > 200:
                    s = s[:200] + '... (truncated, full length = %i)' % len(s)
            l.append('    %-10s %s' % (k + ':', s))
        return string.join(l,'\n')

###

class DefaultMixin:

    """ Mixin that provides default attribute values.

        The namespace returns _default for all undefined attributes
        (except the ones that start with an underscore, see
        __getattr__). The _default is defined as '' for this class.

        The acquisition feature is *not* available for instances of this
        class.

    """
    _default = ''

    def __getattr__(self,name):

        """ Return self._default for all undefined attributes that don't
            start with an underscore.

            For attributes that start with an underscore, raise an
            AttributeError. This is especially important if the
            objects are to be pickled, since the pickle mechanism
            queries a few methods on the objects, which it then calls.
            
        """
        if name[0] == '_':
            raise AttributeError,name
        return self._default

###

class NamespaceWithDefault(DefaultMixin,Namespace):

    """ Namespace class with an infinite number of attributes.

        The namespace returns _default for all undefined attributes
        (except the ones that start with an underscore, see
        __getattr__). The _default is defined as '' for this class.

        The acquisition feature is *not* available for instances of this
        class.

    """

freeze(NamespaceWithDefault)

###

class _NotFound:
    pass
_NotFound = _NotFound()

class FormatNamespaceMixin:

    """ This mixin makes a Namespace class conform to the Python
        format string mechanism.

        The following format specifiers are supported:

        %(attribute)s		- gives the string representation
        			  of .attribute
        %(<eval-string>)s	- evaluates the <eval-string> in the
        			  Namespace dictionary and returns
                                  the string representation

        All standard Python string formatting output types are
        supported, provided that the attribute or evaluation result
        can be converted to these types.
                                  
    """

    def __getitem__(self, item,

                    _NotFound=_NotFound):

        obj = getattr(self, item, _NotFound)
        if obj is not _NotFound:
            return obj
        return eval(item, self.__dict__)

###

class FormatNamespace(FormatNamespaceMixin,
                      Namespace):

    """ Namespace class which supports the Python format string
        lookup mechanism.

        These Namespaces can be used in 'format string' % namespace
        formatting.

        The following format specifiers are supported:

        %(attribute)s		- gives the string representation
        			  of .attribute
        %(<eval-string>)s	- evaluates the <eval-string> in the
        			  Namespace dictionary and returns
                                  the string representation
                                  
        All standard Python string formatting output types are
        supported, provided that the attribute or evaluation result
        can be converted to these types.
                                  
    """
    pass

freeze(FormatNamespace)