• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zopefoundation / transaction / 16399678488

18 Sep 2024 07:25AM UTC coverage: 99.793% (+0.1%) from 99.696%
16399678488

push

github

dataflake
- vb [ci skip]

299 of 306 branches covered (97.71%)

Branch coverage included in aggregate %.

3083 of 3083 relevant lines covered (100.0%)

1.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.65
/src/transaction/tests/savepointsample.py
1
##############################################################################
2
#
3
# Copyright (c) 2004 Zope Foundation and Contributors.
4
# All Rights Reserved.
5
#
6
# This software is subject to the provisions of the Zope Public License,
7
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
8
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11
# FOR A PARTICULAR PURPOSE.
12
#
13
##############################################################################
14
"""Savepoint data manager implementation example.
15

16
Sample data manager implementation that illustrates how to implement
17
savepoints.
18

19
Used by savepoint.rst in the Sphinx docs.
20
"""
21

22
from zope.interface import implementer
1✔
23

24
import transaction.interfaces
1✔
25

26

27
@implementer(transaction.interfaces.IDataManager)
1✔
28
class SampleDataManager:
1✔
29
    """Sample implementation of data manager that doesn't support savepoints
30

31
    This data manager stores named simple values, like strings and numbers.
32
    """
33

34
    def __init__(self, transaction_manager=None):
1✔
35
        if transaction_manager is None:
1!
36
            # Use the thread-local transaction manager if none is provided:
37
            import transaction
1✔
38
            transaction_manager = transaction.manager
1✔
39
        self.transaction_manager = transaction_manager
1✔
40

41
        # Our committed and uncommitted data:
42
        self.committed = {}
1✔
43
        self.uncommitted = self.committed.copy()
1✔
44

45
        # Our transaction state:
46
        #
47
        #   If our uncommitted data is modified, we'll join a transaction
48
        #   and keep track of the transaction we joined.  Any commit
49
        #   related messages we get should be for this same transaction
50
        self.transaction = None
1✔
51

52
        #   What phase, if any, of two-phase commit we are in:
53
        self.tpc_phase = None
1✔
54

55
    # ######################################################################
56
    # Provide a mapping interface to uncommitted data.  We provide
57
    # a basic subset of the interface. DictMixin does the rest.
58

59
    def __getitem__(self, name):
1✔
60
        return self.uncommitted[name]
1✔
61

62
    def __setitem__(self, name, value):
1✔
63
        self._join()  # join the current transaction, if we haven't already
1✔
64
        self.uncommitted[name] = value
1✔
65

66
    def keys(self):
1✔
67
        return self.uncommitted.keys()
1✔
68

69
    __iter__ = keys
1✔
70

71
    def __contains__(self, k):
1✔
72
        return k in self.uncommitted
1✔
73

74
    def __repr__(self):
1✔
75
        return repr(self.uncommitted)
1✔
76

77
    #
78
    #######################################################################
79

80
    #######################################################################
81
    # Transaction methods
82

83
    def _join(self):
1✔
84
        # If this is the first change in the transaction, join the transaction
85
        if self.transaction is None:
1✔
86
            self.transaction = self.transaction_manager.get()
1✔
87
            self.transaction.join(self)
1✔
88

89
    def _resetTransaction(self):
1✔
90
        self.last_note = getattr(self.transaction, 'description', None)
1✔
91
        self.transaction = None
1✔
92
        self.tpc_phase = None
1✔
93

94
    def abort(self, transaction):
1✔
95
        """Throw away changes made before the commit process has started."""
96
        assert ((transaction is self.transaction) or (self.transaction is None)
1✔
97
                ), "Must not change transactions"
98
        assert self.tpc_phase is None, "Must be called outside of tpc"
1✔
99
        self.uncommitted = self.committed.copy()
1✔
100
        self._resetTransaction()
1✔
101

102
    def tpc_begin(self, transaction):
1✔
103
        """Enter two-phase commit."""
104
        assert transaction is self.transaction, "Must not change transactions"
1✔
105
        assert self.tpc_phase is None, "Must be called outside of tpc"
1✔
106
        self.tpc_phase = 1
1✔
107

108
    def commit(self, transaction):
1✔
109
        """Record data modified during the transaction."""
110
        assert transaction is self.transaction, "Must not change transactions"
1✔
111
        assert self.tpc_phase == 1, "Must be called in first phase of tpc"
1✔
112

113
        # In our simple example, we don't need to do anything.
114
        # A more complex data manager would typically write to some sort
115
        # of log.
116

117
    def tpc_vote(self, transaction):
1✔
118
        assert transaction is self.transaction, "Must not change transactions"
1✔
119
        assert self.tpc_phase == 1, "Must be called in first phase of tpc"
1✔
120
        # This particular data manager is always ready to vote.
121
        # Real data managers will usually need to take some steps to
122
        # make sure that the finish will succeed
123
        self.tpc_phase = 2
1✔
124

125
    def tpc_finish(self, transaction):
1✔
126
        assert transaction is self.transaction, "Must not change transactions"
1✔
127
        assert self.tpc_phase == 2, "Must be called in second phase of tpc"
1✔
128
        self.committed = self.uncommitted.copy()
1✔
129
        self._resetTransaction()
1✔
130

131
    def tpc_abort(self, transaction):
1✔
132
        if self.transaction is not None:  # pragma: no cover
133
            # otherwise we're not actually joined.
134
            assert self.tpc_phase is not None, "Must be called inside of tpc"
135
            self.uncommitted = self.committed.copy()
136
            self._resetTransaction()
137

138
    #
139
    #######################################################################
140

141
    #######################################################################
142
    # Other data manager methods
143

144
    def sortKey(self):
1✔
145
        # Commit operations on multiple data managers are performed in
146
        # sort key order.  This important to avoid deadlock when data
147
        # managers are shared among multiple threads or processes and
148
        # use locks to manage that sharing.  We aren't going to bother
149
        # with that here.
150
        return str(id(self))
1✔
151

152
    #
153
    #######################################################################
154

155

156
@implementer(transaction.interfaces.ISavepointDataManager)
1✔
157
class SampleSavepointDataManager(SampleDataManager):
1✔
158
    """Sample implementation of a savepoint-supporting data manager
159

160
    This extends the basic data manager with savepoint support.
161
    """
162

163
    def savepoint(self):
1✔
164
        # When we create the savepoint, we save the existing database state.
165
        return SampleSavepoint(self, self.uncommitted.copy())
1✔
166

167
    def _rollback_savepoint(self, savepoint):
1✔
168
        # When we rollback the savepoint, we restore the saved data.
169
        # Caution:  without the copy(), further changes to the database
170
        # could reflect in savepoint.data, and then `savepoint` would no
171
        # longer contain the originally saved data, and so `savepoint`
172
        # couldn't restore the original state if a rollback to this
173
        # savepoint was done again.  IOW, copy() is necessary.
174
        self.uncommitted = savepoint.data.copy()
1✔
175

176

177
@implementer(transaction.interfaces.IDataManagerSavepoint)
1✔
178
class SampleSavepoint:
1✔
179

180
    def __init__(self, data_manager, data):
1✔
181
        self.data_manager = data_manager
1✔
182
        self.data = data
1✔
183

184
    def rollback(self):
1✔
185
        self.data_manager._rollback_savepoint(self)
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc