Mercurial > babel > old > mirror
comparison 0.8.x/babel/util.py @ 142:4a7af44e6695 stable
Create branch for 0.8.x releases.
author | cmlenz |
---|---|
date | Wed, 20 Jun 2007 10:09:07 +0000 |
parents | |
children | bdf9b34ed1e4 |
comparison
equal
deleted
inserted
replaced
1:bf36ec5f5e50 | 142:4a7af44e6695 |
---|---|
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 from datetime import timedelta, tzinfo | |
17 import os | |
18 import re | |
19 import time | |
20 | |
21 __all__ = ['pathmatch', 'relpath', 'UTC', 'LOCALTZ'] | |
22 __docformat__ = 'restructuredtext en' | |
23 | |
24 def pathmatch(pattern, filename): | |
25 """Extended pathname pattern matching. | |
26 | |
27 This function is similar to what is provided by the ``fnmatch`` module in | |
28 the Python standard library, but: | |
29 | |
30 * can match complete (relative or absolute) path names, and not just file | |
31 names, and | |
32 * also supports a convenience pattern ("**") to match files at any | |
33 directory level. | |
34 | |
35 Examples: | |
36 | |
37 >>> pathmatch('**.py', 'bar.py') | |
38 True | |
39 >>> pathmatch('**.py', 'foo/bar/baz.py') | |
40 True | |
41 >>> pathmatch('**.py', 'templates/index.html') | |
42 False | |
43 | |
44 >>> pathmatch('**/templates/*.html', 'templates/index.html') | |
45 True | |
46 >>> pathmatch('**/templates/*.html', 'templates/foo/bar.html') | |
47 False | |
48 | |
49 :param pattern: the glob pattern | |
50 :param filename: the path name of the file to match against | |
51 :return: `True` if the path name matches the pattern, `False` otherwise | |
52 :rtype: `bool` | |
53 """ | |
54 symbols = { | |
55 '?': '[^/]', | |
56 '?/': '[^/]/', | |
57 '*': '[^/]+', | |
58 '*/': '[^/]+/', | |
59 '**/': '(?:.+/)*?', | |
60 '**': '(?:.+/)*?[^/]+', | |
61 } | |
62 buf = [] | |
63 for idx, part in enumerate(re.split('([?*]+/?)', pattern)): | |
64 if idx % 2: | |
65 buf.append(symbols[part]) | |
66 elif part: | |
67 buf.append(re.escape(part)) | |
68 match = re.match(''.join(buf) + '$', filename.replace(os.sep, '/')) | |
69 return match is not None | |
70 | |
71 | |
72 class odict(dict): | |
73 """Ordered dict implementation. | |
74 | |
75 :see: `http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747` | |
76 """ | |
77 def __init__(self, data=None): | |
78 dict.__init__(self, data or {}) | |
79 self._keys = [] | |
80 | |
81 def __delitem__(self, key): | |
82 dict.__delitem__(self, key) | |
83 self._keys.remove(key) | |
84 | |
85 def __setitem__(self, key, item): | |
86 dict.__setitem__(self, key, item) | |
87 if key not in self._keys: | |
88 self._keys.append(key) | |
89 | |
90 def __iter__(self): | |
91 return iter(self._keys) | |
92 | |
93 def clear(self): | |
94 dict.clear(self) | |
95 self._keys = [] | |
96 | |
97 def copy(self): | |
98 d = odict() | |
99 d.update(self) | |
100 return d | |
101 | |
102 def items(self): | |
103 return zip(self._keys, self.values()) | |
104 | |
105 def keys(self): | |
106 return self._keys[:] | |
107 | |
108 def setdefault(self, key, failobj = None): | |
109 dict.setdefault(self, key, failobj) | |
110 if key not in self._keys: | |
111 self._keys.append(key) | |
112 | |
113 def update(self, dict): | |
114 for (key, val) in dict.items(): | |
115 self[key] = val | |
116 | |
117 def values(self): | |
118 return map(self.get, self._keys) | |
119 | |
120 | |
121 try: | |
122 relpath = os.path.relpath | |
123 except AttributeError: | |
124 def relpath(path, start='.'): | |
125 """Compute the relative path to one path from another. | |
126 | |
127 >>> relpath('foo/bar.txt', '').replace(os.sep, '/') | |
128 'foo/bar.txt' | |
129 >>> relpath('foo/bar.txt', 'foo').replace(os.sep, '/') | |
130 'bar.txt' | |
131 >>> relpath('foo/bar.txt', 'baz').replace(os.sep, '/') | |
132 '../foo/bar.txt' | |
133 | |
134 :return: the relative path | |
135 :rtype: `basestring` | |
136 """ | |
137 start_list = os.path.abspath(start).split(os.sep) | |
138 path_list = os.path.abspath(path).split(os.sep) | |
139 | |
140 # Work out how much of the filepath is shared by start and path. | |
141 i = len(os.path.commonprefix([start_list, path_list])) | |
142 | |
143 rel_list = [os.path.pardir] * (len(start_list) - i) + path_list[i:] | |
144 return os.path.join(*rel_list) | |
145 | |
146 ZERO = timedelta(0) | |
147 | |
148 | |
149 class FixedOffsetTimezone(tzinfo): | |
150 """Fixed offset in minutes east from UTC.""" | |
151 | |
152 def __init__(self, offset, name=None): | |
153 self._offset = timedelta(minutes=offset) | |
154 if name is None: | |
155 name = 'Etc/GMT+%d' % offset | |
156 self.zone = name | |
157 | |
158 def __str__(self): | |
159 return self.zone | |
160 | |
161 def __repr__(self): | |
162 return '<FixedOffset "%s" %s>' % (self.zone, self._offset) | |
163 | |
164 def utcoffset(self, dt): | |
165 return self._offset | |
166 | |
167 def tzname(self, dt): | |
168 return self.zone | |
169 | |
170 def dst(self, dt): | |
171 return ZERO | |
172 | |
173 | |
174 try: | |
175 from pytz import UTC | |
176 except ImportError: | |
177 UTC = FixedOffsetTimezone(0, 'UTC') | |
178 """`tzinfo` object for UTC (Universal Time). | |
179 | |
180 :type: `tzinfo` | |
181 """ | |
182 | |
183 STDOFFSET = timedelta(seconds = -time.timezone) | |
184 if time.daylight: | |
185 DSTOFFSET = timedelta(seconds = -time.altzone) | |
186 else: | |
187 DSTOFFSET = STDOFFSET | |
188 | |
189 DSTDIFF = DSTOFFSET - STDOFFSET | |
190 | |
191 | |
192 class LocalTimezone(tzinfo): | |
193 | |
194 def utcoffset(self, dt): | |
195 if self._isdst(dt): | |
196 return DSTOFFSET | |
197 else: | |
198 return STDOFFSET | |
199 | |
200 def dst(self, dt): | |
201 if self._isdst(dt): | |
202 return DSTDIFF | |
203 else: | |
204 return ZERO | |
205 | |
206 def tzname(self, dt): | |
207 return time.tzname[self._isdst(dt)] | |
208 | |
209 def _isdst(self, dt): | |
210 tt = (dt.year, dt.month, dt.day, | |
211 dt.hour, dt.minute, dt.second, | |
212 dt.weekday(), 0, -1) | |
213 stamp = time.mktime(tt) | |
214 tt = time.localtime(stamp) | |
215 return tt.tm_isdst > 0 | |
216 | |
217 | |
218 LOCALTZ = LocalTimezone() | |
219 """`tzinfo` object for local time-zone. | |
220 | |
221 :type: `tzinfo` | |
222 """ |