module_loading.py 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
  1. from importlib import (
  2. import_module,
  3. )
  4. import operator
  5. from typing import (
  6. Any,
  7. Tuple,
  8. )
  9. def import_string(dotted_path: str) -> Any:
  10. """
  11. Source: django.utils.module_loading
  12. Import a dotted module path and return the attribute/class designated by the
  13. last name in the path. Raise ImportError if the import failed.
  14. """
  15. try:
  16. module_path, class_name = dotted_path.rsplit(".", 1)
  17. except ValueError:
  18. msg = f"{dotted_path} doesn't look like a module path"
  19. raise ImportError(msg)
  20. module = import_module(module_path)
  21. try:
  22. return getattr(module, class_name)
  23. except AttributeError:
  24. msg = f'Module "{module_path}" does not define a "{class_name}" attribute/class'
  25. raise ImportError(msg)
  26. def split_at_longest_importable_path(dotted_path: str) -> Tuple[str, str]:
  27. num_path_parts = len(dotted_path.split("."))
  28. for i in range(1, num_path_parts):
  29. path_parts = dotted_path.rsplit(".", i)
  30. import_part = path_parts[0]
  31. remainder = ".".join(path_parts[1:])
  32. try:
  33. module = import_module(import_part)
  34. except ImportError:
  35. continue
  36. try:
  37. operator.attrgetter(remainder)(module)
  38. except AttributeError:
  39. raise ImportError(
  40. f"Unable to derive appropriate import path for {dotted_path}"
  41. )
  42. else:
  43. return import_part, remainder
  44. else:
  45. return "", dotted_path