Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/Pootle-2.0.0/local_apps/pootle_app/url_state.py
blob: 125c32c93130d6d8f7b89ac12c29ca80b4ddab3d (plain)
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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# 
# Copyright 2009 Zuza Software Foundation
# 
# This file is part of Pootle.
#
# Pootle is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# 
# Pootle is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Pootle; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

class Value(object):
    """Python descriptor for marshalling GET/POST state into Python
    variables and back.

    The descriptor is given a name (see __init__) which it uses to
    read its state from a dictionary (containing GET/POST variables).

    The descriptor stores its state within the object from which it is
    invoked using its own name prefix with an underscore (so if x is a
    descripor in class Foo, and foo is an instance of Foo, then the
    actual value of x is stored in foo._x). If its state is missing
    from its host object (i.e. if _x is not an attribute of foo), the
    descriptor will return its default value (so foo.x will give a
    default value).

    This is a base class which will marshal strings from and to a
    GET/POST dictionary.

    How-To Guide for descriptors: http://users.rcn.com/python/download/Descriptor.htm
    """
    default_value = None

    def __init__(self, var_name):
        self.var_name = var_name

    def _member_name(self):
        """Return the name that we'll use to store this descriptor's
        data under in its host object. A decriptor foo will have its
        data stored under _foo."""
        return '_' + self.var_name

    def __get__(self, obj, type=None):
        return getattr(obj, self._member_name(), self.default_value)

    def __set__(self, obj, value):
        setattr(obj, self._member_name(), value)

    def __delete__(self, obj):
        delattr(obj, self._member_name())

    def add_to_dict(self, obj, dct):
        """If the descriptor's value is not equal to its default
        value, then encode it to a string and store it in the GET/POST
        dictionary C{dct}."""
        value = self.__get__(obj)
        if value != self.default_value:
            dct[self.var_name] = self._encode(value)

    def read_from_params(self, obj, params):
        """Read the value of this descriptor from the GET/POST
        dictionary C{params}, decode it and use it to set the
        descriptor value."""
        try:
            self.__set__(obj, self._decode(params[self.var_name]))
        except KeyError:
            pass

    def _decode(self, value):
        return value

    def _encode(self, value):
        return value

    def __repr__(self):
        return "Value<%s>" % self.var_name

class BooleanValue(Value):
    default_value = False

    def _decode(self, value):
        if value == 'True':
            return True
        else:
            return False

    def _encode(self, value):
        if value:
            return 'True'
        else:
            return 'False'

class IntValue(Value):
    """Read a GET/POST variable into an integer. The descriptor's
    default value is supplied when initializing an IntValue. This
    value is used when the descriptor has no state stored in its host
    object or when the GET/POST variable doesn't decode to an integer.

        >>> class Foo(object):
        ...     bar = IntValue('bar', 1)
        >>> foo = Foo()
        >>> foo.bar
        1
        >>> foo.__class__.bar._decode('10')
        10
        >>> foo.__class__.bar._decode('a')
        1
        >>> foo.__class__.bar._encode(20)
        '20'
        >>> foo.__class__.bar.read_from_params(foo, {'bar': '5'})
        >>> foo.bar
        5
        >>> get_vars = {}
        >>> foo.__class__.bar.add_to_dict(foo, get_vars)
        >>> get_vars
        {'bar': '5'}
    """
    def __init__(self, var_name, default_value):
        super(IntValue, self).__init__(var_name)
        self.default_value = default_value

    def _decode(self, value):
        try:
            return int(value)
        except ValueError:
            return self.default_value

    def _encode(self, value):
        assert isinstance(value, int)
        return str(value)

class ChoiceValue(Value):
    """Read a string GET/POST variable ensuring that it matches one of
    the choices specified when constructing this object. If not, set
    this descriptor value to the first choice.

        >>> class Foo(object):
        ...     bar = ChoiceValue('bar', ('chocolate', 'strawberry', 'caramel'))
        >>> foo = Foo()
        >>> foo.__class__.bar._decode('strawberry')
        'strawberry'
        >>> foo.__class__.bar._decode('banana')
        'chocolate'
    """

    def __init__(self, var_name, choices):
        super(ChoiceValue, self).__init__(var_name)
        self.default_value = choices[0]
        self._choices = choices

    def _decode(self, value):
        if value in self._choices:
            return value
        else:
            return self.default_value

    def _encode(self, value):
        if value in self._choices:
            return value
        else:
            return self.default_value

class ListValue(Value):
    """Read a comma separated GET/POST variable into a Python list of
    strings.

        >>> class Foo(object):
        ...     bar = ListValue('bar')
        >>> foo = Foo()
        >>> foo.__class__.bar._decode('a,b,c')
        ['a', 'b', 'c']
        >>> foo.__class__.bar._encode(['a', 'b', 'c'])
        'a,b,c'
        >>> foo.__class__.bar.read_from_params(foo, {'bar': 'x,y,z'})
        >>> foo.bar
        ['x', 'y', 'z']
        >>> get_vars = {}
        >>> foo.__class__.bar.add_to_dict(foo, get_vars)
        >>> get_vars
        {'bar': 'x,y,z'}
    """
    default_value = []

    def _decode(self, value):
        if value in ('', None):
            return []
        else:
            return value.split(',')

    def _encode(self, value):
        return ','.join(str(item) for item in value)

def get_descriptors(cls, descriptors, visited):
    """Enumerate a class and all its subclasses in a depth-first post
    order traversal and collect all the Python descriptors into the
    list 'descriptors'"""
    for base in cls.__bases__:
        if base not in visited:
            visited.add(base)
            descriptors = get_descriptors(base, descriptors, visited)
    descriptors.extend(descriptor for descriptor in cls.__dict__.itervalues()
                       if isinstance(descriptor, Value))
    return descriptors

class State(object):
    """Base class for classes which, using any of the *Value classes
    as descriptors, will read GET or POST variables intelligently.

    To see an example implementation, look for TranslatePageState."""

    def get_descriptors(self):
        """Return a list of all the *Value descriptors (all defined
        above) that are defined in the class of the current object as
        well as its superclasses.

        Thus, if we have the following subclass:

            >>> class FooState(State):
            ...     bar = IntValue('bar', 0)
            ...     baz = ListValue('baz')
            >>> state = FooState()
            >>> sorted(state.get_descriptors())
            ['bar', 'baz']
        """
        return get_descriptors(self.__class__, [], set())

    def iter_items(self):
        """Iterate through all the *Value descriptors returning
        (descriptor name, descriptor value) pairs.

            >>> class FooState(State):
            ...     bar = IntValue('bar', 0)
            ...     baz = ListValue('baz')
            >>> state = FooState(bar=1, baz=['a', 'b', 'c'])
            >>> sorted(list(state.iter_items()))
            [('bar', 1), ('baz', ['a', 'b', 'c'])]
        """
        for member in self.get_descriptors():
            yield member.var_name, getattr(self, member.var_name)


    def __init__(self, data={}, **initial):
        """Initialize a state object possibly reading initial state
        from C{data} (which is in raw text) and overriding those
        values with the keyword parameters C{initial} (these
        parameters are not raw, so for an IntValue, you'd pass an
        integer).
        """
        for member in self.get_descriptors():
            member.read_from_params(self, data)
        for key, value in initial.iteritems():
            setattr(self, key, value)

    def encode(self):
        """Encode all the state members in this object to a GET/POST
        dictionary."""
        result = {}
        for member in self.get_descriptors():
            member.add_to_dict(self, result)
        return result