1
|
1 # -*- coding: utf-8 -*-
|
|
2 #
|
|
3 # Copyright (C) 2007 Edgewall Software
|
|
4 # All rights reserved.
|
|
5 #
|
|
6 # This software is licensed as described in the file COPYING, which
|
|
7 # you should have received as part of this distribution. The terms
|
|
8 # are also available at http://babel.edgewall.org/wiki/License.
|
|
9 #
|
|
10 # This software consists of voluntary contributions made by many
|
|
11 # individuals. For the exact contribution history, see the revision
|
|
12 # history and logs, available at http://babel.edgewall.org/log/.
|
|
13
|
|
14 """Various utility classes and functions."""
|
|
15
|
30
|
16 from datetime import timedelta, tzinfo
|
1
|
17 import os
|
|
18 import re
|
|
19
|
29
|
20 __all__ = ['default_locale', 'extended_glob', 'relpath', 'LazyProxy', 'UTC']
|
1
|
21 __docformat__ = 'restructuredtext en'
|
|
22
|
13
|
23 def default_locale(kind=None):
|
1
|
24 """Returns the default locale for a given category, based on environment
|
|
25 variables.
|
|
26
|
|
27 :param kind: one of the ``LC_XXX`` environment variable names
|
|
28 :return: the value of the variable, or any of the fallbacks (``LC_ALL`` and
|
|
29 ``LANG``)
|
|
30 :rtype: `str`
|
|
31 """
|
13
|
32 for name in filter(None, (kind, 'LC_ALL', 'LANG')):
|
1
|
33 locale = os.getenv(name)
|
|
34 if locale is not None:
|
|
35 return locale
|
|
36
|
|
37 def extended_glob(pattern, dirname=''):
|
|
38 """Extended pathname pattern expansion.
|
|
39
|
|
40 This function is similar to what is provided by the ``glob`` module in the
|
|
41 Python standard library, but also supports a convenience pattern ("**") to
|
|
42 match files at any directory level.
|
|
43
|
|
44 :param pattern: the glob pattern
|
|
45 :param dirname: the path to the directory in which to search for files
|
|
46 matching the given pattern
|
|
47 :return: an iterator over the absolute filenames of any matching files
|
|
48 :rtype: ``iterator``
|
|
49 """
|
|
50 symbols = {
|
|
51 '?': '[^/]',
|
|
52 '?/': '[^/]/',
|
|
53 '*': '[^/]+',
|
|
54 '*/': '[^/]+/',
|
|
55 '**': '(?:.+/)*?',
|
|
56 '**/': '(?:.+/)*?',
|
|
57 }
|
|
58 buf = []
|
|
59 for idx, part in enumerate(re.split('([?*]+/?)', pattern)):
|
|
60 if idx % 2:
|
|
61 buf.append(symbols[part])
|
|
62 elif part:
|
|
63 buf.append(re.escape(part))
|
|
64 regex = re.compile(''.join(buf) + '$')
|
|
65
|
|
66 absname = os.path.abspath(dirname)
|
|
67 for root, dirnames, filenames in os.walk(absname):
|
|
68 for subdir in dirnames:
|
|
69 if subdir.startswith('.') or subdir.startswith('_'):
|
|
70 dirnames.remove(subdir)
|
|
71 for filename in filenames:
|
|
72 filepath = relpath(
|
|
73 os.path.join(root, filename).replace(os.sep, '/'),
|
|
74 dirname
|
|
75 )
|
|
76 if regex.match(filepath):
|
|
77 yield filepath
|
|
78
|
29
|
79
|
13
|
80 class LazyProxy(object):
|
|
81 """Class for proxy objects that delegate to a specified function to evaluate
|
|
82 the actual object.
|
1
|
83
|
13
|
84 >>> def greeting(name='world'):
|
|
85 ... return 'Hello, %s!' % name
|
|
86 >>> lazy_greeting = LazyProxy(greeting, name='Joe')
|
|
87 >>> print lazy_greeting
|
|
88 Hello, Joe!
|
|
89 >>> u' ' + lazy_greeting
|
|
90 u' Hello, Joe!'
|
|
91 >>> u'(%s)' % lazy_greeting
|
|
92 u'(Hello, Joe!)'
|
1
|
93
|
13
|
94 This can be used, for example, to implement lazy translation functions that
|
|
95 delay the actual translation until the string is actually used. The
|
|
96 rationale for such behavior is that the locale of the user may not always
|
|
97 be available. In web applications, you only know the locale when processing
|
|
98 a request.
|
|
99
|
|
100 The proxy implementation attempts to be as complete as possible, so that
|
|
101 the lazy objects should mostly work as expected, for example for sorting:
|
|
102
|
|
103 >>> greetings = [
|
|
104 ... LazyProxy(greeting, 'world'),
|
|
105 ... LazyProxy(greeting, 'Joe'),
|
|
106 ... LazyProxy(greeting, 'universe'),
|
|
107 ... ]
|
|
108 >>> greetings.sort()
|
|
109 >>> for greeting in greetings:
|
|
110 ... print greeting
|
|
111 Hello, Joe!
|
|
112 Hello, universe!
|
|
113 Hello, world!
|
1
|
114 """
|
13
|
115 __slots__ = ['_func', '_args', '_kwargs', '_value']
|
1
|
116
|
|
117 def __init__(self, func, *args, **kwargs):
|
13
|
118 # Avoid triggering our own __setattr__ implementation
|
|
119 object.__setattr__(self, '_func', func)
|
|
120 object.__setattr__(self, '_args', args)
|
|
121 object.__setattr__(self, '_kwargs', kwargs)
|
|
122 object.__setattr__(self, '_value', None)
|
1
|
123
|
|
124 def value(self):
|
|
125 if self._value is None:
|
13
|
126 value = self._func(*self._args, **self._kwargs)
|
|
127 object.__setattr__(self, '_value', value)
|
1
|
128 return self._value
|
|
129 value = property(value)
|
|
130
|
13
|
131 def __contains__(self, key):
|
|
132 return key in self.value
|
|
133
|
|
134 def __nonzero__(self):
|
|
135 return bool(self.value)
|
|
136
|
|
137 def __dir__(self):
|
|
138 return dir(self.value)
|
|
139
|
|
140 def __iter__(self):
|
|
141 return iter(self.value)
|
|
142
|
|
143 def __len__(self):
|
|
144 return len(self.value)
|
|
145
|
1
|
146 def __str__(self):
|
|
147 return str(self.value)
|
|
148
|
|
149 def __unicode__(self):
|
|
150 return unicode(self.value)
|
|
151
|
|
152 def __add__(self, other):
|
|
153 return self.value + other
|
|
154
|
|
155 def __radd__(self, other):
|
|
156 return other + self.value
|
|
157
|
|
158 def __mod__(self, other):
|
|
159 return self.value % other
|
|
160
|
|
161 def __rmod__(self, other):
|
|
162 return other % self.value
|
|
163
|
|
164 def __mul__(self, other):
|
|
165 return self.value * other
|
|
166
|
|
167 def __rmul__(self, other):
|
|
168 return other * self.value
|
|
169
|
|
170 def __call__(self, *args, **kwargs):
|
|
171 return self.value(*args, **kwargs)
|
|
172
|
13
|
173 def __lt__(self, other):
|
|
174 return self.value < other
|
1
|
175
|
13
|
176 def __le__(self, other):
|
|
177 return self.value <= other
|
1
|
178
|
|
179 def __eq__(self, other):
|
|
180 return self.value == other
|
|
181
|
13
|
182 def __ne__(self, other):
|
|
183 return self.value != other
|
|
184
|
|
185 def __gt__(self, other):
|
|
186 return self.value > other
|
|
187
|
|
188 def __ge__(self, other):
|
|
189 return self.value >= other
|
|
190
|
|
191 def __delattr__(self, name):
|
|
192 delattr(self.value, name)
|
|
193
|
|
194 def __getattr__(self, name):
|
|
195 return getattr(self.value, name)
|
|
196
|
|
197 def __setattr__(self, key, value):
|
|
198 setattr(self.value, name, value)
|
1
|
199
|
|
200 def __delitem__(self, key):
|
13
|
201 del self.value[key]
|
1
|
202
|
|
203 def __getitem__(self, key):
|
13
|
204 return self.value[key]
|
1
|
205
|
|
206 def __setitem__(self, key, value):
|
|
207 self.value[name] = value
|
|
208
|
|
209
|
|
210 try:
|
|
211 relpath = os.path.relpath
|
|
212 except AttributeError:
|
|
213 def relpath(path, start='.'):
|
29
|
214 """Compute the relative path to one path from another.
|
|
215
|
|
216 :return: the relative path
|
|
217 :rtype: `basestring`
|
|
218 """
|
1
|
219 start_list = os.path.abspath(start).split(os.sep)
|
|
220 path_list = os.path.abspath(path).split(os.sep)
|
|
221
|
|
222 # Work out how much of the filepath is shared by start and path.
|
|
223 i = len(os.path.commonprefix([start_list, path_list]))
|
|
224
|
|
225 rel_list = [os.path.pardir] * (len(start_list) - i) + path_list[i:]
|
|
226 return os.path.join(*rel_list)
|
29
|
227
|
|
228 try:
|
|
229 from pytz import UTC
|
|
230 except ImportError:
|
|
231 ZERO = timedelta(0)
|
|
232
|
|
233 class UTC(tzinfo):
|
|
234 """Simple `tzinfo` implementation for UTC."""
|
|
235
|
|
236 def __repr__(self):
|
|
237 return '<UTC>'
|
|
238
|
|
239 def __str__(self):
|
|
240 return 'UTC'
|
|
241
|
|
242 def utcoffset(self, dt):
|
|
243 return ZERO
|
|
244
|
|
245 def tzname(self, dt):
|
|
246 return 'UTC'
|
|
247
|
|
248 def dst(self, dt):
|
|
249 return ZERO
|
|
250
|
|
251 UTC = UTC()
|
|
252 """`tzinfo` object for UTC (Universal Time).
|
|
253
|
|
254 :type: `tzinfo`
|
|
255 """
|