tools.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. import sys
  2. import traceback
  3. # Python 3 compatibility
  4. from ._compat import reduce, as_unicode
  5. CHAR_ESCAPE = u'.'
  6. CHAR_SEPARATOR = u','
  7. def import_module(name, required=True):
  8. """
  9. Import module by name
  10. :param name:
  11. Module name
  12. :param required:
  13. If set to `True` and module was not found - will throw exception.
  14. If set to `False` and module was not found - will return None.
  15. Default is `True`.
  16. """
  17. try:
  18. __import__(name, globals(), locals(), [])
  19. except ImportError:
  20. if not required and module_not_found():
  21. return None
  22. raise
  23. return sys.modules[name]
  24. def import_attribute(name):
  25. """
  26. Import attribute using string reference.
  27. :param name:
  28. String reference.
  29. Raises ImportError or AttributeError if module or attribute do not exist.
  30. Example::
  31. import_attribute('a.b.c.foo')
  32. """
  33. path, attr = name.rsplit('.', 1)
  34. module = __import__(path, globals(), locals(), [attr])
  35. return getattr(module, attr)
  36. def module_not_found(additional_depth=0):
  37. """
  38. Checks if ImportError was raised because module does not exist or
  39. something inside it raised ImportError
  40. :param additional_depth:
  41. supply int of depth of your call if you're not doing
  42. import on the same level of code - f.e., if you call function, which is
  43. doing import, you should pass 1 for single additional level of depth
  44. """
  45. tb = sys.exc_info()[2]
  46. if len(traceback.extract_tb(tb)) > (1 + additional_depth):
  47. return False
  48. return True
  49. def rec_getattr(obj, attr, default=None):
  50. """
  51. Recursive getattr.
  52. :param attr:
  53. Dot delimited attribute name
  54. :param default:
  55. Default value
  56. Example::
  57. rec_getattr(obj, 'a.b.c')
  58. """
  59. try:
  60. return reduce(getattr, attr.split('.'), obj)
  61. except AttributeError:
  62. return default
  63. def get_dict_attr(obj, attr, default=None):
  64. """
  65. Get attribute of the object without triggering its __getattr__.
  66. :param obj:
  67. Object
  68. :param attr:
  69. Attribute name
  70. :param default:
  71. Default value if attribute was not found
  72. """
  73. for obj in [obj] + obj.__class__.mro():
  74. if attr in obj.__dict__:
  75. return obj.__dict__[attr]
  76. return default
  77. def escape(value):
  78. return (as_unicode(value)
  79. .replace(CHAR_ESCAPE, CHAR_ESCAPE + CHAR_ESCAPE)
  80. .replace(CHAR_SEPARATOR, CHAR_ESCAPE + CHAR_SEPARATOR))
  81. def iterencode(iter):
  82. """
  83. Encode enumerable as compact string representation.
  84. :param iter:
  85. Enumerable
  86. """
  87. return ','.join(as_unicode(v)
  88. .replace(CHAR_ESCAPE, CHAR_ESCAPE + CHAR_ESCAPE)
  89. .replace(CHAR_SEPARATOR, CHAR_ESCAPE + CHAR_SEPARATOR)
  90. for v in iter)
  91. def iterdecode(value):
  92. """
  93. Decode enumerable from string presentation as a tuple
  94. """
  95. if not value:
  96. return tuple()
  97. result = []
  98. accumulator = u''
  99. escaped = False
  100. for c in value:
  101. if not escaped:
  102. if c == CHAR_ESCAPE:
  103. escaped = True
  104. continue
  105. elif c == CHAR_SEPARATOR:
  106. result.append(accumulator)
  107. accumulator = u''
  108. continue
  109. else:
  110. escaped = False
  111. accumulator += c
  112. result.append(accumulator)
  113. return tuple(result)