~ntt-pf-lab/nova/monkey_patch_notification

« back to all changes in this revision

Viewing changes to vendor/boto/boto/sdb/db/sequence.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (c) 2010 Chris Moyer http://coredumped.org/
 
2
#
 
3
# Permission is hereby granted, free of charge, to any person obtaining a
 
4
# copy of this software and associated documentation files (the
 
5
# "Software"), to deal in the Software without restriction, including
 
6
# without limitation the rights to use, copy, modify, merge, publish, dis-
 
7
# tribute, sublicense, and/or sell copies of the Software, and to permit
 
8
# persons to whom the Software is furnished to do so, subject to the fol-
 
9
# lowing conditions:
 
10
#
 
11
# The above copyright notice and this permission notice shall be included
 
12
# in all copies or substantial portions of the Software.
 
13
#
 
14
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 
15
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
 
16
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
 
17
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
 
18
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
19
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 
20
# IN THE SOFTWARE.
 
21
 
 
22
from boto.exception import SDBResponseError
 
23
 
 
24
class SequenceGenerator(object):
 
25
    """Generic Sequence Generator object, this takes a single
 
26
    string as the "sequence" and uses that to figure out
 
27
    what the next value in a string is. For example
 
28
    if you give "ABC" and pass in "A" it will give you "B",
 
29
    and if you give it "C" it will give you "AA".
 
30
 
 
31
    If you set "rollover" to True in the above example, passing
 
32
    in "C" would give you "A" again.
 
33
 
 
34
    The Sequence string can be a string or any iterable
 
35
    that has the "index" function and is indexable.
 
36
    """
 
37
    __name__ = "SequenceGenerator"
 
38
 
 
39
    def __init__(self, sequence_string, rollover=False):
 
40
        """Create a new SequenceGenerator using the sequence_string
 
41
        as how to generate the next item.
 
42
 
 
43
        :param sequence_string: The string or list that explains
 
44
        how to generate the next item in the sequence
 
45
        :type sequence_string: str,iterable
 
46
 
 
47
        :param rollover: Rollover instead of incrementing when
 
48
        we hit the end of the sequence
 
49
        :type rollover: bool
 
50
        """
 
51
        self.sequence_string = sequence_string
 
52
        self.sequence_length = len(sequence_string[0])
 
53
        self.rollover = rollover
 
54
        self.last_item = sequence_string[-1]
 
55
        self.__name__ = "%s('%s')" % (self.__class__.__name__, sequence_string)
 
56
 
 
57
    def __call__(self, val, last=None):
 
58
        """Get the next value in the sequence"""
 
59
        # If they pass us in a string that's not at least
 
60
        # the lenght of our sequence, then return the
 
61
        # first element in our sequence
 
62
        if val == None or len(val) < self.sequence_length:
 
63
            return self.sequence_string[0]
 
64
        last_value = val[-self.sequence_length:]
 
65
        if (not self.rollover) and (last_value == self.last_item):
 
66
            val = "%s%s" % (self(val[:-self.sequence_length]), self._inc(last_value))
 
67
        else:
 
68
            val = "%s%s" % (val[:-self.sequence_length], self._inc(last_value))
 
69
        return val
 
70
 
 
71
    def _inc(self, val):
 
72
        """Increment a single value"""
 
73
        assert(len(val) == self.sequence_length)
 
74
        return self.sequence_string[(self.sequence_string.index(val)+1) % len(self.sequence_string)]
 
75
 
 
76
 
 
77
 
 
78
#
 
79
# Simple Sequence Functions
 
80
#
 
81
def increment_by_one(cv=None, lv=None):
 
82
    if cv == None:
 
83
        return 0
 
84
    return cv + 1
 
85
 
 
86
def double(cv=None, lv=None):
 
87
    if cv == None:
 
88
        return 1
 
89
    return cv * 2
 
90
 
 
91
def fib(cv=1, lv=0):
 
92
    """The fibonacci sequence, this incrementer uses the
 
93
    last value"""
 
94
    if cv == None:
 
95
        cv = 1
 
96
    if lv == None:
 
97
        lv = 0
 
98
    return cv + lv
 
99
 
 
100
increment_string = SequenceGenerator("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
 
101
 
 
102
 
 
103
 
 
104
class Sequence(object):
 
105
    """A simple Sequence using the new SDB "Consistent" features
 
106
    Based largly off of the "Counter" example from mitch garnaat:
 
107
    http://bitbucket.org/mitch/stupidbototricks/src/tip/counter.py"""
 
108
 
 
109
 
 
110
    def __init__(self, id=None, domain_name=None, fnc=increment_by_one, init_val=None):
 
111
        """Create a new Sequence, using an optional function to 
 
112
        increment to the next number, by default we just increment by one.
 
113
        Every parameter here is optional, if you don't specify any options
 
114
        then you'll get a new SequenceGenerator with a random ID stored in the
 
115
        default domain that increments by one and uses the default botoweb 
 
116
        environment
 
117
 
 
118
        :param id: Optional ID (name) for this counter
 
119
        :type id: str
 
120
 
 
121
        :param domain_name: Optional domain name to use, by default we get this out of the
 
122
            environment configuration
 
123
        :type domain_name:str
 
124
 
 
125
        :param fnc: Optional function to use for the incrementation, by default we just increment by one
 
126
            There are several functions defined in this module.
 
127
            Your function must accept "None" to get the initial value
 
128
        :type fnc: function, str
 
129
 
 
130
        :param init_val: Initial value, by default this is the first element in your sequence, 
 
131
            but you can pass in any value, even a string if you pass in a function that uses
 
132
            strings instead of ints to increment
 
133
        """
 
134
        self._db = None
 
135
        self._value = None
 
136
        self.last_value = None
 
137
        self.domain_name = domain_name
 
138
        self.id = id
 
139
        if self.id == None:
 
140
            import uuid
 
141
            self.id = str(uuid.uuid4())
 
142
            if init_val == None:
 
143
                init_val = fnc(init_val)
 
144
            self.val = init_val
 
145
 
 
146
        self.item_type = type(fnc(None))
 
147
        self.timestamp = None
 
148
        # Allow us to pass in a full name to a function
 
149
        if type(fnc) == str:
 
150
            from boto.utils import find_class
 
151
            fnc = find_class(fnc)
 
152
        self.fnc = fnc
 
153
 
 
154
    def set(self, val):
 
155
        """Set the value"""
 
156
        import time
 
157
        now = time.time()
 
158
        expected_values = []
 
159
        new_val = {}
 
160
        new_val['timestamp'] = now
 
161
        if self._value != None:
 
162
            new_val['last_value'] = self._value
 
163
            expected_values = ['current_value', str(self._value)]
 
164
        new_val['current_value'] = val
 
165
        try:
 
166
            self.db.put_attributes(self.id, new_val, expected_values=expected_values)
 
167
            self.timestamp = new_val['timestamp']
 
168
        except SDBResponseError, e:
 
169
            if e.status == 409:
 
170
                raise ValueError, "Sequence out of sync"
 
171
            else:
 
172
                raise
 
173
 
 
174
 
 
175
    def get(self):
 
176
        """Get the value"""
 
177
        val = self.db.get_attributes(self.id, consistent_read=True)
 
178
        if val and val.has_key('timestamp'):
 
179
            self.timestamp = val['timestamp']
 
180
        if val and val.has_key('current_value'):
 
181
            self._value = self.item_type(val['current_value'])
 
182
        if val.has_key("last_value") and val['last_value'] != None:
 
183
            self.last_value = self.item_type(val['last_value'])
 
184
        return self._value
 
185
 
 
186
    val = property(get, set)
 
187
 
 
188
    def __repr__(self):
 
189
        return "%s('%s', '%s', '%s.%s', '%s')" % (
 
190
            self.__class__.__name__,
 
191
            self.id,
 
192
            self.domain_name,
 
193
            self.fnc.__module__, self.fnc.__name__,
 
194
            self.val)
 
195
 
 
196
 
 
197
    def _connect(self):
 
198
        """Connect to our domain"""
 
199
        if not self._db:
 
200
            if not self.domain_name:
 
201
                import boto
 
202
                sdb = boto.connect_sdb()
 
203
                self.domain_name = boto.config.get("DB", "sequence_db", boto.config.get("DB", "db_name", "default"))
 
204
            try:
 
205
                self._db = sdb.get_domain(self.domain_name)
 
206
            except SDBResponseError, e:
 
207
                if e.status == 400:
 
208
                    self._db = sdb.create_domain(self.domain_name)
 
209
                else:
 
210
                    raise
 
211
        return self._db
 
212
 
 
213
    db = property(_connect)
 
214
 
 
215
    def next(self):
 
216
        self.val = self.fnc(self.val, self.last_value)
 
217
        return self.val
 
218
 
 
219
    def delete(self):
 
220
        """Remove this sequence"""
 
221
        self.db.delete_attributes(self.id)
 
222
 
 
223
    def __del__(self):
 
224
        self.delete()