This file is indexed.

/usr/share/pyshared/transaction/tests/convenience.txt is in python-transaction 1.1.1-2.

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
Transaction convenience support
===============================

(We *really* need to write proper documentation for the transaction
 package, but I don't want to block the conveniences documented here
 for that.)

with support
------------

We can now use the with statement to define transaction boundaries.

    >>> import transaction.tests.savepointsample
    >>> dm = transaction.tests.savepointsample.SampleSavepointDataManager()
    >>> dm.keys()
    []

We can use the transaction module directly:

    >>> with transaction as t:
    ...     dm['z'] = 1
    ...     t.note('test 1')

    >>> dm['z']
    1

    >>> dm.last_note
    'test 1'

    >>> with transaction:
    ...     dm['z'] = 2
    ...     xxx
    Traceback (most recent call last):
    ...
    NameError: name 'xxx' is not defined

    >>> dm['z']
    1

We can use it with a manager:

    >>> with transaction.manager as t:
    ...     dm['z'] = 3
    ...     t.note('test 3')

    >>> dm['z']
    3

    >>> dm.last_note
    'test 3'

    >>> with transaction:
    ...     dm['z'] = 4
    ...     xxx
    Traceback (most recent call last):
    ...
    NameError: name 'xxx' is not defined

    >>> dm['z']
    3

Retries
-------

Commits can fail for transient reasons, especially conflicts.
Applications will often retry transactions some number of times to
overcome transient failures.  This typically looks something like::

    for i in range(3):
        try:
           with transaction:
               ... some something ...
        except SomeTransientException:
           contine
        else:
           break

This is rather ugly.

Transaction managers provide a helper for this case. To show this,
we'll use a contrived example:


    >>> ntry = 0
    >>> with transaction:
    ...      dm['ntry'] = 0

    >>> import transaction.interfaces
    >>> class Retry(transaction.interfaces.TransientError):
    ...     pass

    >>> for attempt in transaction.manager.attempts():
    ...     with attempt as t:
    ...         t.note('test')
    ...         print dm['ntry'], ntry
    ...         ntry += 1
    ...         dm['ntry'] = ntry
    ...         if ntry % 3:
    ...             raise Retry(ntry)
    0 0
    0 1
    0 2

The raising of a subclass of TransientError is critical here. It's
what signals that the transaction should be retried.  It is generally
up to the data manager to signal that a transaction should try again
by raising a subclass of TransientError (or TransientError itself, of
course).

You shouldn't make any assumptions about the object returned by the
iterator.  (It isn't a transaction or transaction manager, as far as
you know. :)  If you use the ``as`` keyword in the ``with`` statement,
a transaction object will be assigned to the variable named.

By default, it tries 3 times. We can tell it how many times to try:

    >>> for attempt in transaction.manager.attempts(2):
    ...     with attempt:
    ...         ntry += 1
    ...         if ntry % 3:
    ...             raise Retry(ntry)
    Traceback (most recent call last):
    ...
    Retry: 5

It it doesn't succeed in that many times, the exception will be
propagated.

Of course, other errors are propagated directly:

    >>> ntry = 0
    >>> for attempt in transaction.manager.attempts():
    ...     with attempt:
    ...         ntry += 1
    ...         if ntry == 3:
    ...             raise ValueError(ntry)
    Traceback (most recent call last):
    ...
    ValueError: 3

We can use the default transaction manager:

    >>> for attempt in transaction.attempts():
    ...     with attempt as t:
    ...         t.note('test')
    ...         print dm['ntry'], ntry
    ...         ntry += 1
    ...         dm['ntry'] = ntry
    ...         if ntry % 3:
    ...             raise Retry(ntry)
    3 3
    3 4
    3 5

Sometimes, a data manager doesn't raise exceptions directly, but
wraps other other systems that raise exceptions outside of it's
control.  Data  managers can provide a should_retry method that takes
an exception instance and returns True if the transaction should be
attempted again.

    >>> class DM(transaction.tests.savepointsample.SampleSavepointDataManager):
    ...     def should_retry(self, e):
    ...         if 'should retry' in str(e):
    ...             return True

    >>> ntry = 0
    >>> dm2 = DM()
    >>> with transaction:
    ...     dm2['ntry'] = 0
    >>> for attempt in transaction.manager.attempts():
    ...     with attempt:
    ...         print dm['ntry'], ntry
    ...         ntry += 1
    ...         dm['ntry'] = ntry
    ...         dm2['ntry'] = ntry
    ...         if ntry % 3:
    ...             raise ValueError('we really should retry this')
    6 0
    6 1
    6 2

    >>> dm2['ntry']
    3