Skip to content

Test PathFinder/FileFinder/*Loaders as an API #47

@asmodehn

Description

@asmodehn

The simplest way to make your custom importer, should also be the easiest : reusing existing classes directly should not come with surprises.

We should integrate the pattern I use in most FileFinder even if I they dont do much, only to integrate with existing python and work around some pitfalls :

class MyFileFinder(FileFinder):

    def __init__(self, path, *loader_details):
        super(MyFileFinder, self).__init__(path, *loader_details)

    def __repr__(self):
        return 'MyFileFinder({!r})'.format(self.path)

    @classmethod
    def path_hook(cls, *loader_details):
        """A class method which returns a closure to use on sys.path_hook
        which will return an instance using the specified loaders and the path
        called on the closure.

        If the path called on the closure is not a directory, or doesnt contain
         any files with the supported extension, ImportError is raised.

         This is different from default python behavior
         but prevent polluting the cache with custom finders
        """
        def path_hook_for_MyFileFinder(path):
            """Path hook for importlib.machinery.FileFinder."""

            if not (os.path.isdir(path)):
                raise ImportError('only directories are supported')

            exts = [x for ld in loader_details for x in ld[1]]
            if not any(fname.endswith(ext) for fname in os.listdir(path) for ext in exts):
                raise ImportError(
                    'only directories containing {ext} files are supported'.format(ext=", ".join(exts)))
            return cls(path, *loader_details)
        return path_hook_for_MyFileFinder

    def find_spec(self, fullname, target=None):
        """
        Try to find a spec for the specified module.
        :param fullname: the name of the package we are trying to import
        :return: the matching spec, or None if not found.
        """

        # We attempt to load a .my file as a module
        tail_module = fullname.rpartition('.')[2]
        base_path = os.path.join(self.path, tail_module)
        for suffix, loader_class in self._loaders:
            full_path = base_path + suffix
            if os.path.isfile(full_path):  # maybe we need more checks here (importlib filefinder checks its cache...)
                return self._get_spec(loader_class, fullname, full_path, None, target)

        # Otherwise, we try find python modules
        return super(MyFileFinder, self).find_spec(fullname=fullname, target=target)

[...]

There are probably similar changes that would be useful in PathFinder. I haven't used it enough yet to know which ones would be useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions