This file is indexed.

/usr/lib/python2.7/dist-packages/foolscap/copyable.py is in python-foolscap 0.13.1-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
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
# -*- test-case-name: foolscap.test.test_copyable -*-

# this module is responsible for all copy-by-value objects

from zope.interface import interface, implements
from twisted.python import reflect, log
from twisted.python.components import registerAdapter
from twisted.internet import defer

import slicer, tokens
from tokens import BananaError, Violation
from foolscap.constraint import OpenerConstraint, IConstraint, Optional

Interface = interface.Interface

############################################################
# the first half of this file is sending/serialization

class ICopyable(Interface):
    """I represent an object which is passed-by-value across PB connections.
    """

    def getTypeToCopy():
        """Return a string which names the class. This string must match the
        one that gets registered at the receiving end. This is typically a
        URL of some sort, in a namespace which you control."""
    def getStateToCopy():
        """Return a state dictionary (with plain-string keys) which will be
        serialized and sent to the remote end. This state object will be
        given to the receiving object's setCopyableState method."""

class Copyable(object):
    implements(ICopyable)
    # you *must* set 'typeToCopy'

    def getTypeToCopy(self):
        try:
            copytype = self.typeToCopy
        except AttributeError:
            raise RuntimeError("Copyable subclasses must specify 'typeToCopy'")
        return copytype
    def getStateToCopy(self):
        return self.__dict__

class CopyableSlicer(slicer.BaseSlicer):
    """I handle ICopyable objects (things which are copied by value)."""
    def slice(self, streamable, banana):
        self.streamable = streamable
        yield 'copyable'
        copytype = self.obj.getTypeToCopy()
        assert isinstance(copytype, str)
        yield copytype
        state = self.obj.getStateToCopy()
        for k,v in state.iteritems():
            yield k
            yield v
    def describe(self):
        return "<%s>" % self.obj.getTypeToCopy()
registerAdapter(CopyableSlicer, ICopyable, tokens.ISlicer)


class Copyable2(slicer.BaseSlicer):
    # I am my own Slicer. This has more methods than you'd usually want in a
    # base class, but if you can't register an Adapter for a whole class
    # hierarchy then you may have to use it.
    def getTypeToCopy(self):
        return reflect.qual(self.__class__)
    def getStateToCopy(self):
        return self.__dict__
    def slice(self, streamable, banana):
        self.streamable = streamable
        yield 'instance'
        yield self.getTypeToCopy()
        yield self.getStateToCopy()
    def describe(self):
        return "<%s>" % self.getTypeToCopy()

#registerRemoteCopy(typename, factory)
#registerUnslicer(typename, factory)

def registerCopier(klass, copier):
    """This is a shortcut for arranging to serialize third-party clases.
    'copier' must be a callable which accepts an instance of the class you
    want to serialize, and returns a tuple of (typename, state_dictionary).
    If it returns a typename of None, the original class's fully-qualified
    classname is used.
    """
    klassname = reflect.qual(klass)
    class _CopierAdapter:
        implements(ICopyable)
        def __init__(self, original):
            self.nameToCopy, self.state = copier(original)
            if self.nameToCopy is None:
                self.nameToCopy = klassname
        def getTypeToCopy(self):
            return self.nameToCopy
        def getStateToCopy(self):
            return self.state
    registerAdapter(_CopierAdapter, klass, ICopyable)

############################################################
# beyond here is the receiving/deserialization side

class RemoteCopyUnslicer(slicer.BaseUnslicer):
    attrname = None
    attrConstraint = None

    def __init__(self, factory, stateSchema):
        self.factory = factory
        self.schema = stateSchema

    def start(self, count):
        self.d = {}
        self.count = count
        self.deferred = defer.Deferred()
        self.protocol.setObject(count, self.deferred)

    def checkToken(self, typebyte, size):
        if self.attrname == None:
            if typebyte not in (tokens.STRING, tokens.VOCAB):
                raise BananaError("RemoteCopyUnslicer keys must be STRINGs")
        else:
            if self.attrConstraint:
                self.attrConstraint.checkToken(typebyte, size)

    def doOpen(self, opentype):
        if self.attrConstraint:
            self.attrConstraint.checkOpentype(opentype)
        unslicer = self.open(opentype)
        if unslicer:
            if self.attrConstraint:
                unslicer.setConstraint(self.attrConstraint)
        return unslicer

    def receiveChild(self, obj, ready_deferred=None):
        assert not isinstance(obj, defer.Deferred)
        assert ready_deferred is None
        if self.attrname == None:
            attrname = obj
            if self.d.has_key(attrname):
                raise BananaError("duplicate attribute name '%s'" % attrname)
            s = self.schema
            if s:
                accept, self.attrConstraint = s.getAttrConstraint(attrname)
                assert accept
            self.attrname = attrname
        else:
            if isinstance(obj, defer.Deferred):
                # TODO: this is an artificial restriction, and it might
                # be possible to remove it, but I need to think through
                # it carefully first
                raise BananaError("unreferenceable object in attribute")
            self.setAttribute(self.attrname, obj)
            self.attrname = None
            self.attrConstraint = None

    def setAttribute(self, name, value):
        self.d[name] = value

    def receiveClose(self):
        try:
            obj = self.factory(self.d)
        except:
            log.msg("%s.receiveClose: problem in factory %s" %
                    (self.__class__.__name__, self.factory))
            log.err()
            raise
        self.protocol.setObject(self.count, obj)
        self.deferred.callback(obj)
        return obj, None

    def describe(self):
        if self.classname == None:
            return "<??>"
        me = "<%s>" % self.classname
        if self.attrname is None:
            return "%s.attrname??" % me
        else:
            return "%s.%s" % (me, self.attrname)


class NonCyclicRemoteCopyUnslicer(RemoteCopyUnslicer):
    # The Deferred used in RemoteCopyUnslicer (used in case the RemoteCopy
    # is participating in a reference cycle, say 'obj.foo = obj') makes it
    # unsuitable for holding Failures (which cannot be passed through
    # Deferred.callback). Use this class for Failures. It cannot handle
    # reference cycles (they will cause a KeyError when the reference is
    # followed).

    def start(self, count):
        self.d = {}
        self.count = count
        self.gettingAttrname = True

    def receiveClose(self):
        obj = self.factory(self.d)
        return obj, None


class IRemoteCopy(Interface):
    """This interface defines what a RemoteCopy class must do. RemoteCopy
    subclasses are used as factories to create objects that correspond to
    Copyables sent over the wire.

    Note that the constructor of an IRemoteCopy class will be called without
    any arguments.
    """

    def setCopyableState(statedict):
        """I accept an attribute dictionary name/value pairs and use it to
        set my internal state.

        Some of the values may be Deferreds, which are placeholders for the
        as-yet-unreferenceable object which will eventually go there. If you
        receive a Deferred, you are responsible for adding a callback to
        update the attribute when it fires. [note:
        RemoteCopyUnslicer.receiveChild currently has a restriction which
        prevents this from happening, but that may go away in the future]

        Some of the objects referenced by the attribute values may have
        Deferreds in them (e.g. containers which reference recursive tuples).
        Such containers are responsible for updating their own state when
        those Deferreds fire, but until that point their state is still
        subject to change. Therefore you must be careful about how much state
        inspection you perform within this method."""

    stateSchema = interface.Attribute("""I return an AttributeDictConstraint
    object which places restrictions on incoming attribute values. These
    restrictions are enforced as the tokens are received, before the state is
    passed to setCopyableState.""")


# This maps typename to an Unslicer factory
CopyableRegistry = {}
def registerRemoteCopyUnslicerFactory(typename, unslicerfactory,
                                      registry=None):
    """Tell PB that unslicerfactory can be used to handle Copyable objects
    that provide a getTypeToCopy name of 'typename'. 'unslicerfactory' must
    be a callable which takes no arguments and returns an object which
    provides IUnslicer.
    """
    assert callable(unslicerfactory)
    # in addition, it must produce a tokens.IUnslicer . This is safe to do
    # because Unslicers don't do anything significant when they are created.
    test_unslicer = unslicerfactory()
    assert tokens.IUnslicer.providedBy(test_unslicer)
    assert type(typename) is str

    if registry == None:
        registry = CopyableRegistry
    assert not registry.has_key(typename)
    registry[typename] = unslicerfactory

# this keeps track of everything submitted to registerRemoteCopyFactory
debug_CopyableFactories = {}
def registerRemoteCopyFactory(typename, factory, stateSchema=None,
                              cyclic=True, registry=None):
    """Tell PB that 'factory' can be used to handle Copyable objects that
    provide a getTypeToCopy name of 'typename'. 'factory' must be a callable
    which accepts a state dictionary and returns a fully-formed instance.

    'cyclic' is a boolean, which should be set to False to avoid using a
    Deferred to provide the resulting RemoteCopy instance. This is needed to
    deserialize Failures (or instances which inherit from one, like
    CopiedFailure). In exchange for this, it cannot handle reference cycles.
    """
    assert callable(factory)
    debug_CopyableFactories[typename] = (factory, stateSchema, cyclic)
    if cyclic:
        def _RemoteCopyUnslicerFactory():
            return RemoteCopyUnslicer(factory, stateSchema)
        registerRemoteCopyUnslicerFactory(typename,
                                          _RemoteCopyUnslicerFactory,
                                          registry)
    else:
        def _RemoteCopyUnslicerFactoryNonCyclic():
            return NonCyclicRemoteCopyUnslicer(factory, stateSchema)
        registerRemoteCopyUnslicerFactory(typename,
                                          _RemoteCopyUnslicerFactoryNonCyclic,
                                          registry)

# this keeps track of everything submitted to registerRemoteCopy, which may
# be useful when you're wondering what's been auto-registered by the
# RemoteCopy metaclass magic
debug_RemoteCopyClasses = {}
def registerRemoteCopy(typename, remote_copy_class, registry=None):
    """Tell PB that remote_copy_class is the appropriate RemoteCopy class to
    use when deserializing a Copyable sequence that is tagged with
    'typename'. 'remote_copy_class' should be a RemoteCopy subclass or
    implement the same interface, which means its constructor takes no
    arguments and it has a setCopyableState(state) method to actually set the
    instance's state after initialization. It must also have a nonCyclic
    attribute.
    """
    assert IRemoteCopy.implementedBy(remote_copy_class)
    assert type(typename) is str

    debug_RemoteCopyClasses[typename] = remote_copy_class
    def _RemoteCopyFactory(state):
        obj = remote_copy_class()
        obj.setCopyableState(state)
        return obj

    registerRemoteCopyFactory(typename, _RemoteCopyFactory,
                              remote_copy_class.stateSchema,
                              not remote_copy_class.nonCyclic,
                              registry)

class RemoteCopyClass(type):
    # auto-register RemoteCopy classes
    def __init__(self, name, bases, dict):
        type.__init__(self, name, bases, dict)
        # don't try to register RemoteCopy itself
        if name == "RemoteCopy" and _RemoteCopyBase in bases:
            #print "not auto-registering %s %s" % (name, bases)
            return
        if "copytype" not in dict:
            # TODO: provide a file/line-number for the class
            raise RuntimeError("RemoteCopy subclass %s must specify 'copytype'"
                               % name)
        copytype = dict['copytype']
        if copytype:
            registry = dict.get('copyableRegistry', None)
            registerRemoteCopy(copytype, self, registry)

class _RemoteCopyBase:

    implements(IRemoteCopy)

    stateSchema = None # always a class attribute
    nonCyclic = False

    def __init__(self):
        # the constructor will always be called without arguments
        pass

    def setCopyableState(self, state):
        self.__dict__ = state

class RemoteCopyOldStyle(_RemoteCopyBase):
    # note that these will not auto-register for you, because old-style
    # classes do not do metaclass magic
    copytype = None

class RemoteCopy(_RemoteCopyBase, object):
    # Set 'copytype' to a unique string that is shared between the
    # sender-side Copyable and the receiver-side RemoteCopy. This RemoteCopy
    # subclass will be auto-registered using the 'copytype' name. Set
    # copytype to None to disable auto-registration.

    __metaclass__ = RemoteCopyClass
    pass


class AttributeDictConstraint(OpenerConstraint):
    """This is a constraint for dictionaries that are used for attributes.
    All keys are short strings, and each value has a separate constraint.
    It could be used to describe instance state, but could also be used
    to constraint arbitrary dictionaries with string keys.

    Some special constraints are legal here: Optional.
    """
    opentypes = [("attrdict",)]
    name = "AttributeDictConstraint"

    def __init__(self, *attrTuples, **kwargs):
        self.ignoreUnknown = kwargs.get('ignoreUnknown', False)
        self.acceptUnknown = kwargs.get('acceptUnknown', False)
        self.keys = {}
        for name, constraint in (list(attrTuples) +
                                 kwargs.get('attributes', {}).items()):
            assert name not in self.keys.keys()
            self.keys[name] = IConstraint(constraint)

    def getAttrConstraint(self, attrname):
        c = self.keys.get(attrname)
        if c:
            if isinstance(c, Optional):
                c = c.constraint
            return (True, c)
        # unknown attribute
        if self.ignoreUnknown:
            return (False, None)
        if self.acceptUnknown:
            return (True, None)
        raise Violation("unknown attribute '%s'" % attrname)

    def checkObject(self, obj, inbound):
        if type(obj) != type({}):
            raise Violation, "'%s' (%s) is not a Dictionary" % (obj,
                                                                type(obj))
        allkeys = self.keys.keys()
        for k in obj.keys():
            try:
                constraint = self.keys[k]
                allkeys.remove(k)
            except KeyError:
                if not self.ignoreUnknown:
                    raise Violation, "key '%s' not in schema" % k
                else:
                    # hmm. kind of a soft violation. allow it for now.
                    pass
            else:
                constraint.checkObject(obj[k], inbound)

        for k in allkeys[:]:
            if isinstance(self.keys[k], Optional):
                allkeys.remove(k)
        if allkeys:
            raise Violation("object is missing required keys: %s" % \
                            ",".join(allkeys))