1import sys
2
3
4class VendorImporter:
5    """
6    A PEP 302 meta path importer for finding optionally-vendored
7    or otherwise naturally-installed packages from root_name.
8    """
9
10    def __init__(self, root_name, vendored_names=(), vendor_pkg=None):
11        self.root_name = root_name
12        self.vendored_names = set(vendored_names)
13        self.vendor_pkg = vendor_pkg or root_name.replace('extern', '_vendor')
14
15    @property
16    def search_path(self):
17        """
18        Search first the vendor package then as a natural package.
19        """
20        yield self.vendor_pkg + '.'
21        yield ''
22
23    def find_module(self, fullname, path=None):
24        """
25        Return self when fullname starts with root_name and the
26        target module is one vendored through this importer.
27        """
28        root, base, target = fullname.partition(self.root_name + '.')
29        if root:
30            return
31        if not any(map(target.startswith, self.vendored_names)):
32            return
33        return self
34
35    def load_module(self, fullname):
36        """
37        Iterate over the search path to locate and load fullname.
38        """
39        root, base, target = fullname.partition(self.root_name + '.')
40        for prefix in self.search_path:
41            try:
42                extant = prefix + target
43                __import__(extant)
44                mod = sys.modules[extant]
45                sys.modules[fullname] = mod
46                # mysterious hack:
47                # Remove the reference to the extant package/module
48                # on later Python versions to cause relative imports
49                # in the vendor package to resolve the same modules
50                # as those going through this importer.
51                if sys.version_info > (3, 3):
52                    del sys.modules[extant]
53                return mod
54            except ImportError:
55                pass
56        else:
57            raise ImportError(
58                "The '{target}' package is required; "
59                "normally this is bundled with this package so if you get "
60                "this warning, consult the packager of your "
61                "distribution.".format(**locals())
62            )
63
64    def install(self):
65        """
66        Install this importer into sys.meta_path if not already present.
67        """
68        if self not in sys.meta_path:
69            sys.meta_path.append(self)
70
71
72names = 'packaging', 'pyparsing', 'six', 'appdirs'
73VendorImporter(__name__, names).install()
74