This file is indexed.

/usr/share/pyshared/igraph/drawing/metamagic.py is in python-igraph 0.6.5-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
"""Auxiliary classes for the default graph drawer in igraph.

This module contains heavy metaclass magic. If you don't understand
the logic behind these classes, probably you don't need them either.

igraph's default graph drawer uses various data sources to determine
the visual appearance of vertices and edges. These data sources
are the following (in order of precedence):

  - The keyword arguments passed to the L{igraph.plot()} function
    (or to L{igraph.Graph.__plot__()} as a matter of fact, since
    L{igraph.plot()} just passes these attributes on). For instance,
    a keyword argument named C{vertex_label} can be used to set
    the labels of vertices.

  - The attributes of the vertices/edges being drawn. For instance,
    a vertex that has a C{label} attribute will use that label when
    drawn by the default graph drawer.

  - The global configuration of igraph. For instance, if the global
    L{igraph.config.Configuration} instance has a key called
    C{plotting.vertex_color}, that will be used as a default color
    for the vertices.

  - If all else fails, there is a built-in default; for instance,
    the default vertex color is C{"red"}. This is hard-wired in the
    source code.

The logic above can be useful in other graph drawers as well, not
only in the default one, therefore it is refactored into the classes
found in this module. Different graph drawers may inspect different
vertex or edge attributes, hence the classes that collect the attributes
from the various data sources are generated in run-time using a
metaclass called L{AttributeCollectorMeta}. You don't have to use
L{AttributeCollectorMeta} directly, just implement a subclass of
L{AttributeCollectorBase} and it will ensure that the appropriate
metaclass is used. With L{AttributeCollectorBase}, you can use a
simple declarative syntax to specify which attributes you are
interested in. For example::

    class VisualEdgeBuilder(AttributeCollectorBase):
        arrow_size = 1.0
        arrow_width = 1.0
        color = ("black", palette.get)
        width = 1.0

    for edge in VisualEdgeBuilder(graph.es):
        print edge.color

The above class is a visual edge builder -- a class that gives the
visual attributes of the edges of a graph that is specified at
construction time. It specifies that the attributes we are interested
in are C{arrow_size}, C{arrow_width}, C{color} and C{width}; the
default values are also given. For C{color}, we also specify that
a method called {palette.get} should be called on every attribute
value to translate color names to RGB values. For the other three
attributes, C{float} will implicitly be called on all attribute values,
this is inferred from the type of the default value itself.

@see: AttributeCollectorMeta, AttributeCollectorBase
"""

from ConfigParser import NoOptionError
from itertools import izip

from igraph.configuration import Configuration

__all__ = ["AttributeSpecification", "AttributeCollectorBase"]

# pylint: disable-msg=R0903
# R0903: too few public methods
class AttributeSpecification(object):
    """Class that describes how the value of a given attribute should be
    retrieved.
    
    The class contains the following members:
        
        - C{name}: the name of the attribute. This is also used when we
          are trying to get its value from a vertex/edge attribute of a
          graph.
          
        - C{alt_name}: alternative name of the attribute. This is used
          when we are trying to get its value from a Python dict or an
          L{igraph.Configuration} object. If omitted at construction time,
          it will be equal to C{name}.
          
        - C{default}: the default value of the attribute when none of
          the sources we try can provide a meaningful value.
          
        - C{transform}: optional transformation to be performed on the
          attribute value. If C{None} or omitted, it defaults to the
          type of the default value.

        - C{func}: when given, this function will be called with an
          index in order to derive the value of the attribute.
    """

    __slots__ = ("name", "alt_name", "default", "transform", "accessor",
                 "func")

    def __init__(self, name, default=None, alt_name=None, transform=None,
                 func=None):
        if isinstance(default, tuple):
            default, transform = default

        self.name = name
        self.default = default
        self.alt_name = alt_name or name
        self.transform = transform or None
        self.func = func
        self.accessor = None

        if self.transform and not hasattr(self.transform, "__call__"):
            raise TypeError, "transform must be callable"

        if self.transform is None and self.default is not None:
            self.transform = type(self.default)


class AttributeCollectorMeta(type):
    """Metaclass for attribute collector classes
    
    Classes that use this metaclass are intended to collect vertex/edge
    attributes from various sources (a Python dict, a vertex/edge sequence,
    default values from the igraph configuration and such) in a given
    order of precedence. See the module documentation for more details.
    This metaclass enables the user to use a simple declarative syntax
    to specify which attributes he is interested in. For each vertex/edge
    attribute, a corresponding class attribute must be defined with a
    value that describes the default value of that attribute if no other
    data source provides us with any suitable value. The default value
    can also be a tuple; in that case, the first element of the tuple
    is the actual default value, the second element is a converter
    function that will convert the attribute values to a format expected
    by the caller who uses the class being defined.

    There is a special class attribute called C{_kwds_prefix}; this is
    not used as an attribute declaration. It can contain a string which
    will be used to derive alternative names for the attributes when
    the attribute is accessed in a Python dict. This is useful in many
    situations; for instance, the default graph drawer would want to access
    the vertex colors using the C{color} vertex attribute, but when
    it looks at the keyword arguments passed to the original call of
    L{igraph.Graph.__plot__}, the C{vertex_color} keyword argument should
    be looked up because we also have colors for the edges. C{_kwds_prefix}
    will be prepended to the attribute names when they are looked up in
    a dict of keyword arguments.

    If you require a more fine-tuned behaviour, you can assign an
    L{AttributeSpecification} instance to a class attribute directly.

    @see: AttributeCollectorBase
    """

    def __new__(mcs, name, bases, attrs):
        attr_specs = []
        for attr, value in attrs.iteritems():
            if attr.startswith("_") or hasattr(value, "__call__"):
                continue
            if isinstance(value, AttributeSpecification):
                attr_spec = value
            elif isinstance(value, dict):
                attr_spec = AttributeSpecification(attr, **value)
            else:
                attr_spec = AttributeSpecification(attr, value)
            attr_specs.append(attr_spec)

        prefix = attrs.get("_kwds_prefix", None)
        if prefix:
            for attr_spec in attr_specs:
                if attr_spec.name == attr_spec.alt_name:
                    attr_spec.alt_name = "%s%s" % (prefix, attr_spec.name)

        attrs["_attributes"] = attr_specs
        attrs["Element"] = mcs.record_generator(
                "%s.Element" % name,
                (attr_spec.name for attr_spec in attr_specs)
        )

        return super(AttributeCollectorMeta, mcs).__new__(mcs, \
                name, bases, attrs)

    @classmethod
    def record_generator(mcs, name, slots):
        """Generates a simple class that has the given slots and nothing else"""
        class Element(object):
            """A simple class that holds the attributes collected by the
            attribute collector"""
            __slots__ = tuple(slots)
            def __init__(self, attrs=()):
                for attr, value in attrs:
                    setattr(self, attr, value)
        Element.__name__ = name
        return Element


class AttributeCollectorBase(object):
    """Base class for attribute collector subclasses. Classes that inherit
    this class may use a declarative syntax to specify which vertex or edge
    attributes they intend to collect. See L{AttributeCollectorMeta} for
    the details.
    """

    __metaclass__ = AttributeCollectorMeta

    def __init__(self, seq, kwds = None):
        """Constructs a new attribute collector that uses the given
        vertex/edge sequence and the given dict as data sources.

        @param seq: an L{igraph.VertexSeq} or L{igraph.EdgeSeq} class
          that will be used as a data source for attributes.
        @param kwds: a Python dict that will be used to override the
          attributes collected from I{seq} if necessary.
        """
        elt = self.__class__.Element
        self._cache = [elt() for _ in xrange(len(seq))]

        self.seq = seq
        self.kwds = kwds or {}

        for attr_spec in self._attributes:
            values = self._collect_attributes(attr_spec) 
            attr_name = attr_spec.name
            for cache_elt, val in izip(self._cache, values):
                setattr(cache_elt, attr_name, val)

    def _collect_attributes(self, attr_spec, config=None):
        """Collects graph visualization attributes from various sources.

        This method can be used to collect the attributes required for graph
        visualization from various sources. Attribute value sources are:

          - A specific value of a Python dict belonging to a given key. This dict
            is given by the argument M{self.kwds} at construction time, and
            the name of the key is determined by the argument specification
            given in M{attr_spec}.

          - A vertex or edge sequence of a graph, given in M{self.seq}

          - The global configuration, given in M{config}

          - A default value when all other sources fail to provide the value.
            This is also given in M{attr_spec}.

        @param  attr_spec: an L{AttributeSpecification} object which contains
                           the name of the attribute when it is coming from a
                           list of Python keyword arguments, the name of the
                           attribute when it is coming from the graph attributes
                           directly, the default value of the attribute and an
                           optional callable transformation to call on the values.
                           This can be used to ensure that the attributes are of
                           a given type.
        @param  config:    a L{Configuration} object to be used for determining the
                           defaults if all else fails. If C{None}, the global
                           igraph configuration will be used
        @return: the collected attributes
        """
        kwds = self.kwds
        seq = self.seq

        n = len(seq)

        # Special case if the attribute name is "label" 
        if attr_spec.name == "label":
            if attr_spec.alt_name in kwds and kwds[attr_spec.alt_name] is None:
                return [None] * n

        # If the attribute uses an external callable to derive the attribute
        # values, call it and store the results
        if attr_spec.func is not None:
            func = attr_spec.func
            result = [func(i) for i in xrange(n)]
            return result

        # Get the configuration object
        if config is None:
            config = Configuration.instance()

        # Fetch the defaults from the vertex/edge sequence
        try:
            attrs = seq[attr_spec.name]
        except KeyError:
            attrs = None

        # Override them from the keyword arguments (if any)
        result = kwds.get(attr_spec.alt_name, None)
        if attrs:
            if not result:
                result = attrs
            else:
                if isinstance(result, str):
                    result = [result] * n
                try:
                    len(result)
                except TypeError:
                    result = [result] * n
                result = [result[idx] or attrs[idx] \
                          for idx in xrange(len(result))]

        # Special case for string overrides, strings are not treated
        # as sequences here
        if isinstance(result, str):
            result = [result] * n

        # If the result is still not a sequence, make it one
        try:
            len(result)
        except TypeError:
            result = [result] * n

        # If it is not a list, ensure that it is a list
        if not hasattr(result, "extend"):
            result = list(result)

        # Ensure that the length is n
        while len(result) < n:
            if len(result) <= n/2:
                result.extend(result)
            else:
                result.extend(result[0:(n-len(result))])

        # By now, the length of the result vector should be n as requested
        # Get the configuration defaults
        try:
            default = config["plotting.%s" % attr_spec.alt_name]
        except NoOptionError:
            default = None

        if default is None:
            default = attr_spec.default

        # Fill the None values with the default values
        for idx in xrange(len(result)):
            if result[idx] is None:
                result[idx] = default

        # Finally, do the transformation
        if attr_spec.transform is not None:
            transform = attr_spec.transform
            result = [transform(x) for x in result]

        return result


    def __getitem__(self, index):
        """Returns the collected attributes of the vertex/edge with the
        given index."""
        # pylint: disable-msg=E1101
        # E1101: instance has no '_attributes' member
        return self._cache[index]

    def __len__(self):
        return len(self.seq)