__init__.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/env python
  3. import random
  4. import string
  5. from contextlib import contextmanager
  6. import six
  7. from kombu.five import monotonic
  8. from six.moves.urllib import parse as urlparse
  9. import logging
  10. __all__ = ('Map')
  11. from typing import TYPE_CHECKING
  12. logger = logging.getLogger(__name__)
  13. class Map(dict):
  14. """
  15. 提供字典的dot访问模式
  16. Example:
  17. m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
  18. """
  19. def __init__(self, *args, **kwargs):
  20. super(Map, self).__init__(*args, **kwargs)
  21. for arg in args:
  22. if isinstance(arg, dict):
  23. for k, v in arg.items():
  24. if isinstance(v, dict):
  25. v = Map(v)
  26. self[k] = v
  27. if kwargs:
  28. for k, v in kwargs.items():
  29. if isinstance(v, dict):
  30. v = Map(v)
  31. self[k] = v
  32. def __getattr__(self, attr):
  33. return self[attr]
  34. def __setattr__(self, key, value):
  35. self.__setitem__(key, value)
  36. def __getitem__(self, key):
  37. if key not in self.__dict__:
  38. super(Map, self).__setitem__(key, {})
  39. self.__dict__.update({key: Map()})
  40. return self.__dict__[key]
  41. def __setitem__(self, key, value):
  42. super(Map, self).__setitem__(key, value)
  43. self.__dict__.update({key: value})
  44. def __delattr__(self, item):
  45. self.__delitem__(item)
  46. def __delitem__(self, key):
  47. super(Map, self).__delitem__(key)
  48. del self.__dict__[key]
  49. def to_text(value, encoding='utf-8'):
  50. """Convert value to unicode, default encoding is utf-8
  51. :param value: Value to be converted
  52. :param encoding: Desired encoding
  53. """
  54. if not value:
  55. return ''
  56. if isinstance(value, six.text_type):
  57. return value
  58. if isinstance(value, six.binary_type):
  59. return value.decode(encoding)
  60. return six.text_type(value)
  61. def to_binary(value, encoding='utf-8'):
  62. """Convert value to binary string, default encoding is utf-8
  63. :param value: Value to be converted
  64. :param encoding: Desired encoding
  65. """
  66. if not value:
  67. return b''
  68. if isinstance(value, six.binary_type):
  69. return value
  70. if isinstance(value, six.text_type):
  71. return value.encode(encoding)
  72. return to_text(value).encode(encoding)
  73. def timezone(zone):
  74. """Try to get timezone using pytz or python-dateutil
  75. :param zone: timezone str
  76. :return: timezone tzinfo or None
  77. """
  78. try:
  79. # noinspection PyUnresolvedReferences
  80. import pytz
  81. return pytz.timezone(zone)
  82. except ImportError:
  83. pass
  84. try:
  85. from dateutil.tz import gettz
  86. return gettz(zone)
  87. except ImportError:
  88. return None
  89. class ObjectDict(dict):
  90. """Makes a dictionary behave like an object, with attribute-style access.
  91. """
  92. def __getattr__(self, key):
  93. if key in self:
  94. return self[key]
  95. return None
  96. def __setattr__(self, key, value):
  97. self[key] = value
  98. def random_string(length=16):
  99. rule = string.ascii_letters + string.digits
  100. rand_list = random.sample(rule, length)
  101. return ''.join(rand_list)
  102. def get_querystring(uri):
  103. """Get Querystring information from uri.
  104. :param uri: uri
  105. :return: querystring info or {}
  106. """
  107. parts = urlparse.urlsplit(uri)
  108. return urlparse.parse_qs(parts.query)
  109. def byte2int(c):
  110. if six.PY2:
  111. return ord(c)
  112. return c
  113. if TYPE_CHECKING:
  114. class MemcacheProxy(object):
  115. def add(self, key, value, timeout=0, version=None):
  116. pass
  117. def get(self, key, default=None, version=None):
  118. pass
  119. def set(self, key, value, timeout=0, version=None):
  120. pass
  121. def delete(self, key, version=None):
  122. pass
  123. def get_many(self, keys, version=None):
  124. pass
  125. def incr(self, key, delta=1, version=None):
  126. pass
  127. def decr(self, key, delta=1, version=None):
  128. pass
  129. @contextmanager
  130. def my_memcache_lock(mc, key, value, expire=60 * 10):
  131. # type: (MemcacheProxy, str, str, int)->None
  132. """
  133. Example usage
  134. ```
  135. with my_memcache_lock(cache, lock_id, self.app.oid) as acquired:
  136. if acquired:
  137. return Feed.objects.import_feed(feed_url).url
  138. logger.debug(
  139. 'Feed %s is already being imported by another worker', feed_url)
  140. ```
  141. :param mc:
  142. :param key:
  143. :param value:
  144. :param expire
  145. :return:
  146. """
  147. value = str(value)
  148. timeout_at = monotonic() + expire - 3
  149. status = mc.add(key, value, expire)
  150. logger.debug('memcache_lock add result is: {}; key is: {}'.format(status, key))
  151. try:
  152. yield status
  153. finally:
  154. # memcache delete is very slow, but we have to use it to take
  155. # advantage of using add() for atomic locking
  156. if monotonic() < timeout_at and status:
  157. # don't release the lock if we exceeded the timeout
  158. # to lessen the chance of releasing an expired lock
  159. # owned by someone else
  160. # also don't release the lock if we didn't acquire it
  161. result = mc.delete(key)
  162. logger.debug('memcache_lock delete result is: {}; key is: {}'.format(result, key))