You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

74 line
2.3KB

  1. import importlib.util
  2. import sys
  3. class VendorImporter:
  4. """
  5. A PEP 302 meta path importer for finding optionally-vendored
  6. or otherwise naturally-installed packages from root_name.
  7. """
  8. def __init__(self, root_name, vendored_names=(), vendor_pkg=None):
  9. self.root_name = root_name
  10. self.vendored_names = set(vendored_names)
  11. self.vendor_pkg = vendor_pkg or root_name.replace('extern', '_vendor')
  12. @property
  13. def search_path(self):
  14. """
  15. Search first the vendor package then as a natural package.
  16. """
  17. yield self.vendor_pkg + '.'
  18. yield ''
  19. def _module_matches_namespace(self, fullname):
  20. """Figure out if the target module is vendored."""
  21. root, base, target = fullname.partition(self.root_name + '.')
  22. return not root and any(map(target.startswith, self.vendored_names))
  23. def load_module(self, fullname):
  24. """
  25. Iterate over the search path to locate and load fullname.
  26. """
  27. root, base, target = fullname.partition(self.root_name + '.')
  28. for prefix in self.search_path:
  29. try:
  30. extant = prefix + target
  31. __import__(extant)
  32. mod = sys.modules[extant]
  33. sys.modules[fullname] = mod
  34. return mod
  35. except ImportError:
  36. pass
  37. else:
  38. raise ImportError(
  39. "The '{target}' package is required; "
  40. "normally this is bundled with this package so if you get "
  41. "this warning, consult the packager of your "
  42. "distribution.".format(**locals())
  43. )
  44. def create_module(self, spec):
  45. return self.load_module(spec.name)
  46. def exec_module(self, module):
  47. pass
  48. def find_spec(self, fullname, path=None, target=None):
  49. """Return a module spec for vendored names."""
  50. return (
  51. importlib.util.spec_from_loader(fullname, self)
  52. if self._module_matches_namespace(fullname) else None
  53. )
  54. def install(self):
  55. """
  56. Install this importer into sys.meta_path if not already present.
  57. """
  58. if self not in sys.meta_path:
  59. sys.meta_path.append(self)
  60. names = 'packaging', 'pyparsing', 'appdirs'
  61. VendorImporter(__name__, names).install()