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