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 """
Copyright (C) 2012-2017 Edgewall Software