This file is indexed.

/usr/lib/python3/dist-packages/persistent/persistence.py is in python3-persistent 4.1.1-1build2.

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
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
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
##############################################################################
#
# Copyright (c) 2011 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
import sys

from zope.interface import implementer

from persistent.interfaces import IPersistent
from persistent.interfaces import GHOST
from persistent.interfaces import UPTODATE
from persistent.interfaces import CHANGED
from persistent.interfaces import STICKY
from persistent.interfaces import OID_TYPE
from persistent.interfaces import SERIAL_TYPE
from persistent.timestamp import TimeStamp
from persistent.timestamp import _ZERO
from persistent._compat import copy_reg
from persistent._compat import intern

from . import ring

_INITIAL_SERIAL = _ZERO


# Bitwise flags
_CHANGED = 0x0001
_STICKY = 0x0002

_OGA = object.__getattribute__
_OSA = object.__setattr__

# These names can be used from a ghost without causing it to be
# activated. These are standardized with the C implementation
SPECIAL_NAMES = ('__class__',
                 '__del__',
                 '__dict__',
                 '__of__',
                 '__setstate__',)

# And this is an implementation detail of this class; it holds
# the standard names plus the slot names, allowing for just one
# check in __getattribute__
_SPECIAL_NAMES = set(SPECIAL_NAMES)

@implementer(IPersistent)
class Persistent(object):
    """ Pure Python implmentation of Persistent base class
    """
    __slots__ = ('__jar', '__oid', '__serial', '__flags', '__size', '__ring',)

    def __new__(cls, *args, **kw):
        inst = super(Persistent, cls).__new__(cls)
        # We bypass the __setattr__ implementation of this object
        # at __new__ time, just like the C implementation does. This
        # makes us compatible with subclasses that want to access
        # properties like _p_changed in their setattr implementation
        _OSA(inst, '_Persistent__jar', None)
        _OSA(inst, '_Persistent__oid', None)
        _OSA(inst, '_Persistent__serial', None)
        _OSA(inst, '_Persistent__flags', None)
        _OSA(inst, '_Persistent__size', 0)
        _OSA(inst, '_Persistent__ring', None)
        return inst

    # _p_jar:  see IPersistent.
    def _get_jar(self):
        return _OGA(self, '_Persistent__jar')

    def _set_jar(self, value):
        jar = _OGA(self, '_Persistent__jar')
        if self._p_is_in_cache(jar) and value is not None and jar != value:
            # The C implementation only forbids changing the jar
            # if we're already in a cache. Match its error message
            raise ValueError('can not change _p_jar of cached object')

        if _OGA(self, '_Persistent__jar') != value:
            _OSA(self, '_Persistent__jar', value)
            _OSA(self, '_Persistent__flags', 0)

    def _del_jar(self):
        jar = _OGA(self, '_Persistent__jar')
        if jar is not None:
            if self._p_is_in_cache(jar):
                raise ValueError("can't delete _p_jar of cached object")
            _OSA(self, '_Persistent__jar', None)
            _OSA(self, '_Persistent__flags', None)

    _p_jar = property(_get_jar, _set_jar, _del_jar)

    # _p_oid:  see IPersistent.
    def _get_oid(self):
        return _OGA(self, '_Persistent__oid')

    def _set_oid(self, value):
        if value == _OGA(self, '_Persistent__oid'):
            return
        # The C implementation allows *any* value to be
        # used as the _p_oid.
        #if value is not None:
        #    if not isinstance(value, OID_TYPE):
        #        raise ValueError('Invalid OID type: %s' % value)
        # The C implementation only forbids changing the OID
        # if we're in a cache, regardless of what the current
        # value or jar is
        if self._p_is_in_cache():
            # match the C error message
            raise ValueError('can not change _p_oid of cached object')
        _OSA(self, '_Persistent__oid', value)

    def _del_oid(self):
        jar = _OGA(self, '_Persistent__jar')
        oid = _OGA(self, '_Persistent__oid')
        if jar is not None:
            if oid and jar._cache.get(oid):
                raise ValueError('Cannot delete _p_oid of cached object')
        _OSA(self, '_Persistent__oid', None)

    _p_oid = property(_get_oid, _set_oid, _del_oid)

    # _p_serial:  see IPersistent.
    def _get_serial(self):
        serial = _OGA(self, '_Persistent__serial')
        if serial is not None:
            return serial
        return _INITIAL_SERIAL

    def _set_serial(self, value):
        if not isinstance(value, SERIAL_TYPE):
            raise ValueError('Invalid SERIAL type: %s' % value)
        if len(value) != 8:
            raise ValueError('SERIAL must be 8 octets')
        _OSA(self, '_Persistent__serial', value)

    def _del_serial(self):
        _OSA(self, '_Persistent__serial', None)

    _p_serial = property(_get_serial, _set_serial, _del_serial)

    # _p_changed:  see IPersistent.
    def _get_changed(self):
        if _OGA(self, '_Persistent__jar') is None:
            return False
        flags = _OGA(self, '_Persistent__flags')
        if flags is None: # ghost
            return None
        return bool(flags & _CHANGED)

    def _set_changed(self, value):
        if _OGA(self, '_Persistent__flags') is None:
            if value:
                self._p_activate()
                self._p_set_changed_flag(value)
        else:
            if value is None: # -> ghost
                self._p_deactivate()
            else:
                self._p_set_changed_flag(value)

    def _del_changed(self):
        self._p_invalidate()

    _p_changed = property(_get_changed, _set_changed, _del_changed)

    # _p_mtime
    def _get_mtime(self):
        # The C implementation automatically unghostifies the object
        # when _p_mtime is accessed.
        self._p_activate()
        self._p_accessed()
        serial = _OGA(self, '_Persistent__serial')
        if serial is not None:
            ts = TimeStamp(serial)
            return ts.timeTime()

    _p_mtime = property(_get_mtime)

    # _p_state
    def _get_state(self):
        # Note the use of OGA and caching to avoid recursive calls to __getattribute__:
        # __getattribute__ calls _p_accessed calls cache.mru() calls _p_state
        if _OGA(self, '_Persistent__jar') is None:
            return UPTODATE
        flags = _OGA(self, '_Persistent__flags')
        if flags is None:
            return GHOST
        if flags & _CHANGED:
            result = CHANGED
        else:
            result = UPTODATE
        if flags & _STICKY:
            return STICKY
        return result

    _p_state = property(_get_state)

    # _p_estimated_size:  XXX don't want to reserve the space?
    def _get_estimated_size(self):
        return _OGA(self, '_Persistent__size') * 64

    def _set_estimated_size(self, value):
        if isinstance(value, int):
            if value < 0:
                raise ValueError('_p_estimated_size must not be negative')
            _OSA(self, '_Persistent__size', _estimated_size_in_24_bits(value))
        else:
            raise TypeError("_p_estimated_size must be an integer")

    def _del_estimated_size(self):
        _OSA(self, '_Persistent__size', 0)

    _p_estimated_size = property(
        _get_estimated_size, _set_estimated_size, _del_estimated_size)

    # The '_p_sticky' property is not (yet) part of the API:  for now,
    # it exists to simplify debugging and testing assertions.
    def _get_sticky(self):
        flags = _OGA(self, '_Persistent__flags')
        if flags is None:
            return False
        return bool(flags & _STICKY)
    def _set_sticky(self, value):
        flags = _OGA(self, '_Persistent__flags')
        if flags is None:
            raise ValueError('Ghost')
        if value:
            flags |= _STICKY
        else:
            flags &= ~_STICKY
        _OSA(self, '_Persistent__flags', flags)
    _p_sticky = property(_get_sticky, _set_sticky)

    # The '_p_status' property is not (yet) part of the API:  for now,
    # it exists to simplify debugging and testing assertions.
    def _get_status(self):
        if _OGA(self, '_Persistent__jar') is None:
            return 'unsaved'
        flags = _OGA(self, '_Persistent__flags')
        if flags is None:
            return 'ghost'
        if flags & _STICKY:
            return 'sticky'
        if flags & _CHANGED:
            return 'changed'
        return 'saved'

    _p_status = property(_get_status)

    # Methods from IPersistent.
    def __getattribute__(self, name):
        """ See IPersistent.
        """
        oga = _OGA
        if (not name.startswith('_p_') and
            name not in _SPECIAL_NAMES):
            if oga(self, '_Persistent__flags') is None:
                oga(self, '_p_activate')()
            oga(self, '_p_accessed')()
        return oga(self, name)

    def __setattr__(self, name, value):
        special_name = (name in _SPECIAL_NAMES or
                        name.startswith('_p_'))
        volatile = name.startswith('_v_')
        if not special_name:
            if _OGA(self, '_Persistent__flags') is None:
                _OGA(self, '_p_activate')()
            if not volatile:
                _OGA(self, '_p_accessed')()
        _OSA(self, name, value)
        if (_OGA(self, '_Persistent__jar') is not None and
            _OGA(self, '_Persistent__oid') is not None and
            not special_name and
            not volatile):
            before = _OGA(self, '_Persistent__flags')
            after = before | _CHANGED
            if before != after:
                _OSA(self, '_Persistent__flags', after)
                _OGA(self, '_p_register')()

    def __delattr__(self, name):
        special_name = (name in _SPECIAL_NAMES or
                        name.startswith('_p_'))
        if not special_name:
            if _OGA(self, '_Persistent__flags') is None:
                _OGA(self, '_p_activate')()
            _OGA(self, '_p_accessed')()
            before = _OGA(self, '_Persistent__flags')
            after = before | _CHANGED
            if before != after:
                _OSA(self, '_Persistent__flags', after)
                if (_OGA(self, '_Persistent__jar') is not None and
                    _OGA(self, '_Persistent__oid') is not None):
                    _OGA(self, '_p_register')()
        object.__delattr__(self, name)

    def _slotnames(self):
        slotnames = copy_reg._slotnames(type(self))
        return [x for x in slotnames
                   if not x.startswith('_p_') and
                      not x.startswith('_v_') and
                      not x.startswith('_Persistent__') and
                      x not in Persistent.__slots__]

    def __getstate__(self):
        """ See IPersistent.
        """
        idict = getattr(self, '__dict__', None)
        slotnames = self._slotnames()
        if idict is not None:
            d = dict([x for x in idict.items()
                         if not x[0].startswith('_p_') and
                            not x[0].startswith('_v_')])
        else:
            d = None
        if slotnames:
            s = {}
            for slotname in slotnames:
                value = getattr(self, slotname, self)
                if value is not self:
                    s[slotname] = value
            return d, s
        return d

    def __setstate__(self, state):
        """ See IPersistent.
        """
        if isinstance(state,tuple):
            inst_dict, slots = state
        else:
            inst_dict, slots = state, ()
        idict = getattr(self, '__dict__', None)
        if inst_dict is not None:
            if idict is None:
                raise TypeError('No instance dict')
            idict.clear()
            for k, v in inst_dict.items():
                # Normally the keys for instance attributes are interned.
                # Do that here, but only if it is possible to do so.
                idict[intern(k) if type(k) is str else k] = v
        slotnames = self._slotnames()
        if slotnames:
            for k, v in slots.items():
                setattr(self, k, v)

    def __reduce__(self):
        """ See IPersistent.
        """
        gna = getattr(self, '__getnewargs__', lambda: ())
        return (copy_reg.__newobj__,
                (type(self),) + gna(), self.__getstate__())

    def _p_activate(self):
        """ See IPersistent.
        """
        oga = _OGA
        before = oga(self, '_Persistent__flags')
        if before is None: # Only do this if we're a ghost
            # Begin by marking up-to-date in case we bail early
            _OSA(self, '_Persistent__flags', 0)
            jar = oga(self, '_Persistent__jar')
            if jar is None:
                return
            oid = oga(self, '_Persistent__oid')
            if oid is None:
                return

            # If we're actually going to execute a set-state,
            # mark as changed to prevent any recursive call
            # (actually, our earlier check that we're a ghost should
            # prevent this, but the C implementation sets it to changed
            # while calling jar.setstate, and this is observable to clients).
            # The main point of this is to prevent changes made during
            # setstate from registering the object with the jar.
            _OSA(self, '_Persistent__flags', CHANGED)
            try:
                jar.setstate(self)
            except:
                _OSA(self, '_Persistent__flags', before)
                raise
            else:
                # If we succeed, no matter what the implementation
                # of setstate did, mark ourself as up-to-date. The
                # C implementation unconditionally does this.
                _OSA(self, '_Persistent__flags', 0) # up-to-date

    # In the C implementation, _p_invalidate winds up calling
    # _p_deactivate. There are ZODB tests that depend on this;
    # it's not documented but there may be code in the wild
    # that does as well

    def _p_deactivate(self):
        """ See IPersistent.
        """
        flags = _OGA(self, '_Persistent__flags')
        if flags is not None and not flags:
            self._p_invalidate_deactivate_helper()

    def _p_invalidate(self):
        """ See IPersistent.
        """
        # If we think we have changes, we must pretend
        # like we don't so that deactivate does its job
        _OSA(self, '_Persistent__flags', 0)
        self._p_deactivate()

    def _p_invalidate_deactivate_helper(self):
        jar = _OGA(self, '_Persistent__jar')
        if jar is None:
            return

        if _OGA(self, '_Persistent__flags') is not None:
            _OSA(self, '_Persistent__flags', None)
        idict = getattr(self, '__dict__', None)
        if idict is not None:
            idict.clear()
        # Implementation detail: deactivating/invalidating
        # updates the size of the cache (if we have one)
        # by telling it this object no longer takes any bytes
        # (-1 is a magic number to compensate for the implementation,
        # which always adds one to the size given)
        try:
            cache = jar._cache
        except AttributeError:
            pass
        else:
            cache.update_object_size_estimation(_OGA(self, '_Persistent__oid'), -1)
            # See notes in PickleCache.sweep for why we have to do this
            cache._persistent_deactivate_ran = True

    def _p_getattr(self, name):
        """ See IPersistent.
        """
        if name.startswith('_p_') or name in _SPECIAL_NAMES:
            return True
        self._p_activate()
        self._p_accessed()
        return False

    def _p_setattr(self, name, value):
        """ See IPersistent.
        """
        if name.startswith('_p_'):
            setattr(self, name, value)
            return True
        self._p_activate()
        self._p_accessed()
        return False

    def _p_delattr(self, name):
        """ See IPersistent.
        """
        if name.startswith('_p_'):
            delattr(self, name)
            return True
        self._p_activate()
        self._p_accessed()
        return False

    # Helper methods:  not APIs:  we name them with '_p_' to bypass
    # the __getattribute__ bit which bumps the cache.
    def _p_register(self):
        jar = _OGA(self, '_Persistent__jar')
        if jar is not None and _OGA(self, '_Persistent__oid') is not None:
            jar.register(self)

    def _p_set_changed_flag(self, value):
        if value:
            before = _OGA(self, '_Persistent__flags')
            after = before | _CHANGED
            if before != after:
                self._p_register()
            _OSA(self, '_Persistent__flags', after)
        else:
            flags = _OGA(self, '_Persistent__flags')
            flags &= ~_CHANGED
            _OSA(self, '_Persistent__flags', flags)


    def _p_accessed(self):
        # Notify the jar's pickle cache that we have been accessed.
        # This relies on what has been (until now) an implementation
        # detail, the '_cache' attribute of the jar.  We made it a
        # private API to avoid the cycle of keeping a reference to
        # the cache on the persistent object.

        # The below is the equivalent of this, but avoids
        # several recursive through __getattribute__, especially for _p_state,
        # and benchmarks much faster
        #
        # if(self.__jar is  None or
        #    self.__oid is None or
        #    self._p_state < 0 ): return

        oga = _OGA
        jar = oga(self, '_Persistent__jar')
        if jar is None:
            return
        oid = oga(self, '_Persistent__oid')
        if oid is None:
            return
        flags = oga(self, '_Persistent__flags')
        if flags is None: # ghost
            return


        # The KeyError arises in ZODB: ZODB.serialize.ObjectWriter
        # can assign a jar and an oid to newly seen persistent objects,
        # but because they are newly created, they aren't in the
        # pickle cache yet. There doesn't seem to be a way to distinguish
        # that at this level, all we can do is catch it.
        # The AttributeError arises in ZODB test cases
        try:
            jar._cache.mru(oid)
        except (AttributeError,KeyError):
            pass


    def _p_is_in_cache(self, jar=None):
        oid = _OGA(self, '_Persistent__oid')
        if not oid:
            return False

        jar = jar or _OGA(self, '_Persistent__jar')
        cache = getattr(jar, '_cache', None)
        if cache is not None:
            return cache.get(oid) is self

def _estimated_size_in_24_bits(value):
    if value > 1073741696:
        return 16777215
    return (value//64) + 1

_SPECIAL_NAMES.update([intern('_Persistent' + x) for x in Persistent.__slots__])