five.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. # -*- coding: utf-8 -*-
  2. """Python 2/3 compatibility utilities."""
  3. import operator
  4. import sys
  5. from importlib import import_module
  6. from types import ModuleType
  7. # extends vine.five
  8. from vine import five
  9. from vine.five import * # noqa
  10. from vine.five import __all__ as _all_five
  11. # bloody flake8
  12. items = five.items
  13. string_t = five.string_t
  14. try:
  15. from functools import reduce
  16. except ImportError:
  17. pass
  18. __all__ = [
  19. 'class_property', 'reclassmethod', 'create_module', 'recreate_module',
  20. ]
  21. __all__ += _all_five
  22. # ############# Module Generation ##########################
  23. # Utilities to dynamically
  24. # recreate modules, either for lazy loading or
  25. # to create old modules at runtime instead of
  26. # having them litter the source tree.
  27. # import fails in python 2.5. fallback to reduce in stdlib
  28. DEFAULT_ATTRS = {'__file__', '__path__', '__doc__', '__all__'}
  29. def getappattr(path):
  30. """Gets attribute from the current_app recursively,
  31. e.g. getappattr('amqp.get_task_consumer')``."""
  32. from celery import current_app
  33. return current_app._rgetattr(path)
  34. def _compat_periodic_task_decorator(*args, **kwargs):
  35. from celery.task import periodic_task
  36. return periodic_task(*args, **kwargs)
  37. class class_property:
  38. def __init__(self, getter=None, setter=None):
  39. if getter is not None and not isinstance(getter, classmethod):
  40. getter = classmethod(getter)
  41. if setter is not None and not isinstance(setter, classmethod):
  42. setter = classmethod(setter)
  43. self.__get = getter
  44. self.__set = setter
  45. info = getter.__get__(object) # just need the info attrs.
  46. self.__doc__ = info.__doc__
  47. self.__name__ = info.__name__
  48. self.__module__ = info.__module__
  49. def __get__(self, obj, type=None):
  50. if obj and type is None:
  51. type = obj.__class__
  52. return self.__get.__get__(obj, type)()
  53. def __set__(self, obj, value):
  54. if obj is None:
  55. return self
  56. return self.__set.__get__(obj)(value)
  57. def setter(self, setter):
  58. return self.__class__(self.__get, setter)
  59. def reclassmethod(method):
  60. return classmethod(method)
  61. class LazyModule(ModuleType):
  62. _compat_modules = ()
  63. _all_by_module = {}
  64. _direct = {}
  65. _object_origins = {}
  66. def __getattr__(self, name):
  67. if name in self._object_origins:
  68. module = __import__(self._object_origins[name], None, None, [name])
  69. for item in self._all_by_module[module.__name__]:
  70. setattr(self, item, getattr(module, item))
  71. return getattr(module, name)
  72. elif name in self._direct: # pragma: no cover
  73. module = __import__(self._direct[name], None, None, [name])
  74. setattr(self, name, module)
  75. return module
  76. return ModuleType.__getattribute__(self, name)
  77. def __dir__(self):
  78. return list(set(self.__all__) | DEFAULT_ATTRS)
  79. def __reduce__(self):
  80. return import_module, (self.__name__,)
  81. def create_module(name, attrs, cls_attrs=None, pkg=None,
  82. base=LazyModule, prepare_attr=None):
  83. fqdn = '.'.join([pkg.__name__, name]) if pkg else name
  84. cls_attrs = {} if cls_attrs is None else cls_attrs
  85. pkg, _, modname = name.rpartition('.')
  86. cls_attrs['__module__'] = pkg
  87. attrs = {
  88. attr_name: (prepare_attr(attr) if prepare_attr else attr)
  89. for attr_name, attr in items(attrs)
  90. }
  91. module = sys.modules[fqdn] = type(modname, (base,), cls_attrs)(name)
  92. module.__dict__.update(attrs)
  93. return module
  94. def recreate_module(name, compat_modules=(), by_module={}, direct={},
  95. base=LazyModule, **attrs):
  96. old_module = sys.modules[name]
  97. origins = get_origins(by_module)
  98. _all = tuple(set(reduce(
  99. operator.add,
  100. [tuple(v) for v in [compat_modules, origins, direct, attrs]],
  101. )))
  102. cattrs = dict(
  103. _compat_modules=compat_modules,
  104. _all_by_module=by_module, _direct=direct,
  105. _object_origins=origins,
  106. __all__=_all,
  107. )
  108. new_module = create_module(name, attrs, cls_attrs=cattrs, base=base)
  109. return old_module, new_module
  110. def get_origins(defs):
  111. origins = {}
  112. for module, attrs in items(defs):
  113. origins.update({attr: module for attr in attrs})
  114. return origins