PageRenderTime 43ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/distribution/libraries/Babel-1.0dev-py3.2/babel/util.py

https://github.com/tictactatic/Superdesk
Python | 377 lines | 326 code | 10 blank | 41 comment | 13 complexity | 393700d9e11a263b72ae155d5ecf724f MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-3.0, GPL-2.0
  1. # -*- coding: utf-8 -*-
  2. #
  3. # Copyright (C) 2007-2011 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. """Various utility classes and functions."""
  14. import codecs
  15. from datetime import timedelta, tzinfo
  16. import os
  17. import re
  18. import sys
  19. import textwrap
  20. import time
  21. from babel.compat import PY3, binary_type, text_type, b
  22. missing = object()
  23. __all__ = ['distinct', 'pathmatch', 'relpath', 'wraptext', 'odict', 'UTC',
  24. 'LOCALTZ']
  25. __docformat__ = 'restructuredtext en'
  26. def distinct(iterable):
  27. """Yield all items in an iterable collection that are distinct.
  28. Unlike when using sets for a similar effect, the original ordering of the
  29. items in the collection is preserved by this function.
  30. >>> print(list(distinct([1, 2, 1, 3, 4, 4])))
  31. [1, 2, 3, 4]
  32. >>> print(list(distinct('foobar')))
  33. ['f', 'o', 'b', 'a', 'r']
  34. :param iterable: the iterable collection providing the data
  35. :return: the distinct items in the collection
  36. :rtype: ``iterator``
  37. """
  38. seen = set()
  39. for item in iter(iterable):
  40. if item not in seen:
  41. yield item
  42. seen.add(item)
  43. # Regexp to match python magic encoding line
  44. PYTHON_MAGIC_COMMENT_re = re.compile(
  45. b(r'[ \t\f]* \# .* coding[=:][ \t]*([-\w.]+)'), re.VERBOSE)
  46. def parse_encoding(fp):
  47. """Deduce the encoding of a source file from magic comment.
  48. It does this in the same way as the `Python interpreter`__
  49. .. __: http://docs.python.org/ref/encodings.html
  50. The ``fp`` argument should be a seekable file object.
  51. (From Jeff Dairiki)
  52. """
  53. pos = fp.tell()
  54. fp.seek(0)
  55. try:
  56. line1 = fp.readline()
  57. if isinstance(line1, text_type):
  58. line1 = line1.encode()
  59. has_bom = line1.startswith(codecs.BOM_UTF8)
  60. if has_bom:
  61. line1 = line1[len(codecs.BOM_UTF8):]
  62. m = PYTHON_MAGIC_COMMENT_re.match(line1)
  63. if not m:
  64. try:
  65. import parser
  66. parser.suite(line1.decode())
  67. except (ImportError, SyntaxError):
  68. # Either it's a real syntax error, in which case the source is
  69. # not valid python source, or line2 is a continuation of line1,
  70. # in which case we don't want to scan line2 for a magic
  71. # comment.
  72. pass
  73. else:
  74. line2 = fp.readline()
  75. if isinstance(line2, text_type):
  76. line2 = line2.encode()
  77. m = PYTHON_MAGIC_COMMENT_re.match(line2)
  78. if has_bom:
  79. if m:
  80. raise SyntaxError(
  81. "python refuses to compile code with both a UTF8 "
  82. "byte-order-mark and a magic encoding comment")
  83. return 'utf_8'
  84. elif m:
  85. return m.group(1)
  86. else:
  87. return None
  88. finally:
  89. fp.seek(pos)
  90. def pathmatch(pattern, filename):
  91. """Extended pathname pattern matching.
  92. This function is similar to what is provided by the ``fnmatch`` module in
  93. the Python standard library, but:
  94. * can match complete (relative or absolute) path names, and not just file
  95. names, and
  96. * also supports a convenience pattern ("**") to match files at any
  97. directory level.
  98. Examples:
  99. >>> pathmatch('**.py', 'bar.py')
  100. True
  101. >>> pathmatch('**.py', 'foo/bar/baz.py')
  102. True
  103. >>> pathmatch('**.py', 'templates/index.html')
  104. False
  105. >>> pathmatch('**/templates/*.html', 'templates/index.html')
  106. True
  107. >>> pathmatch('**/templates/*.html', 'templates/foo/bar.html')
  108. False
  109. :param pattern: the glob pattern
  110. :param filename: the path name of the file to match against
  111. :return: `True` if the path name matches the pattern, `False` otherwise
  112. :rtype: `bool`
  113. """
  114. symbols = {
  115. '?': '[^/]',
  116. '?/': '[^/]/',
  117. '*': '[^/]+',
  118. '*/': '[^/]+/',
  119. '**/': '(?:.+/)*?',
  120. '**': '(?:.+/)*?[^/]+',
  121. }
  122. buf = []
  123. for idx, part in enumerate(re.split('([?*]+/?)', pattern)):
  124. if idx % 2:
  125. buf.append(symbols[part])
  126. elif part:
  127. buf.append(re.escape(part))
  128. match = re.match(''.join(buf) + '$', filename.replace(os.sep, '/'))
  129. return match is not None
  130. class TextWrapper(textwrap.TextWrapper):
  131. wordsep_re = re.compile(
  132. r'(\s+|' # any whitespace
  133. r'(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))' # em-dash
  134. )
  135. def wraptext(text, width=70, initial_indent='', subsequent_indent=''):
  136. """Simple wrapper around the ``textwrap.wrap`` function in the standard
  137. library. This version does not wrap lines on hyphens in words.
  138. :param text: the text to wrap
  139. :param width: the maximum line width
  140. :param initial_indent: string that will be prepended to the first line of
  141. wrapped output
  142. :param subsequent_indent: string that will be prepended to all lines save
  143. the first of wrapped output
  144. :return: a list of lines
  145. :rtype: `list`
  146. """
  147. wrapper = TextWrapper(width=width, initial_indent=initial_indent,
  148. subsequent_indent=subsequent_indent,
  149. break_long_words=False)
  150. return wrapper.wrap(text)
  151. if sys.version_info >= (3, 1):
  152. import collections
  153. class odict(collections.OrderedDict):
  154. """
  155. Ordered dict implementation.
  156. As per PEP 372, an ordered dict was added to Python 3.1 and above
  157. Use that here, as the odict below did not work for py3.2
  158. """
  159. def __init__(self, data=None):
  160. collections.OrderedDict.__init__(self, data or {})
  161. else:
  162. class odict(dict):
  163. """Ordered dict implementation.
  164. :see: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/107747
  165. """
  166. def __init__(self, data=None):
  167. dict.__init__(self, data or {})
  168. self._keys = dict.keys(self)
  169. def __delitem__(self, key):
  170. dict.__delitem__(self, key)
  171. self._keys.remove(key)
  172. def __setitem__(self, key, item):
  173. dict.__setitem__(self, key, item)
  174. if key not in self._keys:
  175. self._keys.append(key)
  176. def __iter__(self):
  177. return iter(self._keys)
  178. iterkeys = __iter__
  179. def clear(self):
  180. dict.clear(self)
  181. self._keys = []
  182. def copy(self):
  183. d = odict()
  184. d.update(self)
  185. return d
  186. def items(self):
  187. if PY3:
  188. return list(zip(self._keys, list(self.values())))
  189. else:
  190. return zip(self._keys, self.values())
  191. def iteritems(self):
  192. if PY3:
  193. return zip(self._keys, iter(self.values()))
  194. else:
  195. from itertools import izip
  196. return izip(self._keys, self.itervalues())
  197. def keys(self):
  198. return self._keys[:]
  199. def pop(self, key, default=missing):
  200. if default is missing:
  201. return dict.pop(self, key)
  202. elif key not in self:
  203. return default
  204. self._keys.remove(key)
  205. return dict.pop(self, key, default)
  206. def popitem(self, key):
  207. self._keys.remove(key)
  208. return dict.popitem(key)
  209. def setdefault(self, key, failobj = None):
  210. dict.setdefault(self, key, failobj)
  211. if key not in self._keys:
  212. self._keys.append(key)
  213. def update(self, dict):
  214. for (key, val) in dict.items():
  215. self[key] = val
  216. def values(self):
  217. if PY3:
  218. return list(map(self.get, self._keys))
  219. else:
  220. return map(self.get, self._keys)
  221. def itervalues(self):
  222. if PY3:
  223. return map(self.get, self._keys)
  224. else:
  225. from itertools import imap
  226. return imap(self.get, self._keys)
  227. try:
  228. relpath = os.path.relpath
  229. except AttributeError:
  230. def relpath(path, start='.'):
  231. """Compute the relative path to one path from another.
  232. >>> relpath('foo/bar.txt', '').replace(os.sep, '/')
  233. 'foo/bar.txt'
  234. >>> relpath('foo/bar.txt', 'foo').replace(os.sep, '/')
  235. 'bar.txt'
  236. >>> relpath('foo/bar.txt', 'baz').replace(os.sep, '/')
  237. '../foo/bar.txt'
  238. :return: the relative path
  239. :rtype: `basestring`
  240. """
  241. start_list = os.path.abspath(start).split(os.sep)
  242. path_list = os.path.abspath(path).split(os.sep)
  243. # Work out how much of the filepath is shared by start and path.
  244. i = len(os.path.commonprefix([start_list, path_list]))
  245. rel_list = [os.path.pardir] * (len(start_list) - i) + path_list[i:]
  246. return os.path.join(*rel_list)
  247. ZERO = timedelta(0)
  248. class FixedOffsetTimezone(tzinfo):
  249. """Fixed offset in minutes east from UTC."""
  250. def __init__(self, offset, name=None):
  251. self._offset = timedelta(minutes=offset)
  252. if name is None:
  253. name = 'Etc/GMT+%d' % offset
  254. self.zone = name
  255. def __str__(self):
  256. return self.zone
  257. def __repr__(self):
  258. return '<FixedOffset "%s" %s>' % (self.zone, self._offset)
  259. def utcoffset(self, dt):
  260. return self._offset
  261. def tzname(self, dt):
  262. return self.zone
  263. def dst(self, dt):
  264. return ZERO
  265. try:
  266. from pytz import UTC
  267. except ImportError:
  268. UTC = FixedOffsetTimezone(0, 'UTC')
  269. """`tzinfo` object for UTC (Universal Time).
  270. :type: `tzinfo`
  271. """
  272. STDOFFSET = timedelta(seconds = -time.timezone)
  273. if time.daylight:
  274. DSTOFFSET = timedelta(seconds = -time.altzone)
  275. else:
  276. DSTOFFSET = STDOFFSET
  277. DSTDIFF = DSTOFFSET - STDOFFSET
  278. class LocalTimezone(tzinfo):
  279. def utcoffset(self, dt):
  280. if self._isdst(dt):
  281. return DSTOFFSET
  282. else:
  283. return STDOFFSET
  284. def dst(self, dt):
  285. if self._isdst(dt):
  286. return DSTDIFF
  287. else:
  288. return ZERO
  289. def tzname(self, dt):
  290. return time.tzname[self._isdst(dt)]
  291. def _isdst(self, dt):
  292. tt = (dt.year, dt.month, dt.day,
  293. dt.hour, dt.minute, dt.second,
  294. dt.weekday(), 0, -1)
  295. stamp = time.mktime(tt)
  296. tt = time.localtime(stamp)
  297. return tt.tm_isdst > 0
  298. LOCALTZ = LocalTimezone()
  299. """`tzinfo` object for local time-zone.
  300. :type: `tzinfo`
  301. """