Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

loading.py 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import sys
  2. import traceback
  3. from django.conf import settings
  4. from django.core.exceptions import ImproperlyConfigured
  5. from django.db.models import get_model
  6. class AppNotFoundError(Exception):
  7. pass
  8. class ClassNotFoundError(Exception):
  9. pass
  10. def get_class(module_label, classname):
  11. """
  12. Dynamically import a single class from the given module.
  13. This is a simple wrapper around `get_classes` for the case of loading a
  14. single class.
  15. Args:
  16. module_label (str): Module label comprising the app label and the
  17. module name, separated by a dot. For example, 'catalogue.forms'.
  18. classname (str): Name of the class to be imported.
  19. Returns:
  20. The requested class object or `None` if it can't be found
  21. """
  22. return get_classes(module_label, [classname])[0]
  23. def get_classes(module_label, classnames):
  24. """
  25. Dynamically import a list of classes from the given module.
  26. This works by looping over ``INSTALLED_APPS`` and looking for a match
  27. against the passed module label. If the requested class can't be found in
  28. the matching module, then we attempt to import it from the corresponding
  29. core Oscar app (assuming the matched module isn't in Oscar).
  30. This is very similar to ``django.db.models.get_model`` function for
  31. dynamically loading models. This function is more general though as it can
  32. load any class from the matching app, not just a model.
  33. Args:
  34. module_label (str): Module label comprising the app label and the
  35. module name, separated by a dot. For example, 'catalogue.forms'.
  36. classname (str): Name of the class to be imported.
  37. Returns:
  38. The requested class object or ``None`` if it can't be found
  39. Examples:
  40. Load a single class:
  41. >>> get_class('basket.forms', 'BasketLineForm')
  42. oscar.apps.basket.forms.BasketLineForm
  43. Load a list of classes:
  44. >>> get_classes('basket.forms', ['BasketLineForm', 'AddToBasketForm'])
  45. [oscar.apps.basket.forms.BasketLineForm,
  46. oscar.apps.basket.forms.AddToBasketForm]
  47. Raises:
  48. AppNotFoundError: If no app is found in ``INSTALLED_APPS`` that matches
  49. the passed module label.
  50. ImportError: If the attempted import of a class raises an
  51. ``ImportError``, it is re-raised
  52. """
  53. app_module_path = _get_app_module_path(module_label)
  54. if not app_module_path:
  55. raise AppNotFoundError("No app found matching '%s'" % module_label)
  56. # Check if app is in oscar
  57. if app_module_path.split('.')[0] == 'oscar':
  58. # Using core oscar class
  59. module_path = 'oscar.apps.%s' % module_label
  60. imported_module = __import__(module_path, fromlist=classnames)
  61. return _pluck_classes([imported_module], classnames)
  62. # App must be local - check if module is in local app (it could be in
  63. # oscar's)
  64. app_label = module_label.split('.')[0]
  65. if '.' in app_module_path:
  66. base_package = app_module_path.rsplit('.' + app_label, 1)[0]
  67. local_app = "%s.%s" % (base_package, module_label)
  68. else:
  69. local_app = module_label
  70. try:
  71. imported_local_module = __import__(local_app, fromlist=classnames)
  72. except ImportError:
  73. # There are 2 reasons why there is ImportError:
  74. # 1. local_app does not exist
  75. # 2. local_app exists but is corrupted (ImportError inside of the app)
  76. #
  77. # Obviously, for the reason #1 we want to fall back to use Oscar app.
  78. # For the reason #2 we want to propagate error (the dev obviously wants
  79. # to override app and not use Oscar app)
  80. #
  81. # ImportError does not provide easy way to distinguish those two cases.
  82. # Fortunately, the traceback of the ImportError starts at __import__
  83. # statement. If the traceback has more than one frame, it means that
  84. # application was found and ImportError originates within the local app
  85. __, __, exc_traceback = sys.exc_info()
  86. frames = traceback.extract_tb(exc_traceback)
  87. if len(frames) > 1:
  88. raise
  89. # Module not in local app
  90. imported_local_module = {}
  91. oscar_app = "oscar.apps.%s" % module_label
  92. try:
  93. imported_oscar_module = __import__(oscar_app, fromlist=classnames)
  94. except ImportError:
  95. # Oscar does not have this application, can't fallback to it
  96. imported_oscar_module = None
  97. return _pluck_classes([imported_local_module, imported_oscar_module],
  98. classnames)
  99. def _pluck_classes(modules, classnames):
  100. klasses = []
  101. for classname in classnames:
  102. klass = None
  103. for module in modules:
  104. if hasattr(module, classname):
  105. klass = getattr(module, classname)
  106. break
  107. if not klass:
  108. packages = [m.__name__ for m in modules]
  109. raise ClassNotFoundError("No class '%s' found in %s" % (
  110. classname, ", ".join(packages)))
  111. klasses.append(klass)
  112. return klasses
  113. def _get_app_module_path(module_label):
  114. app_name = module_label.rsplit(".", 1)[0]
  115. for installed_app in settings.INSTALLED_APPS:
  116. if installed_app.endswith(app_name):
  117. return installed_app
  118. return None
  119. def get_profile_class():
  120. """
  121. Return the profile model class
  122. """
  123. setting = getattr(settings, 'AUTH_PROFILE_MODULE', None)
  124. if setting is None:
  125. return None
  126. app_label, model_name = settings.AUTH_PROFILE_MODULE.split('.')
  127. profile_class = get_model(app_label, model_name)
  128. if not profile_class:
  129. raise ImproperlyConfigured("Can't import profile model")
  130. return profile_class
  131. def feature_hidden(feature_name):
  132. """
  133. Test if a certain Oscar feature is disabled.
  134. """
  135. return (feature_name is not None and
  136. feature_name in settings.OSCAR_HIDDEN_FEATURES)