Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
readers.py195 linesDownload Raw Back to importlib_resources
1import collections2import contextlib3import itertools4import pathlib5import operator6import re7import warnings8 9from . import abc10 11from ._itertools import only12from .compat.py39 import ZipPath13 14 15def remove_duplicates(items):16    return iter(collections.OrderedDict.fromkeys(items))17 18 19class FileReader(abc.TraversableResources):20    def __init__(self, loader):21        self.path = pathlib.Path(loader.path).parent22 23    def resource_path(self, resource):24        """25        Return the file system path to prevent26        `resources.path()` from creating a temporary27        copy.28        """29        return str(self.path.joinpath(resource))30 31    def files(self):32        return self.path33 34 35class ZipReader(abc.TraversableResources):36    def __init__(self, loader, module):37        _, _, name = module.rpartition('.')38        self.prefix = loader.prefix.replace('\\', '/') + name + '/'39        self.archive = loader.archive40 41    def open_resource(self, resource):42        try:43            return super().open_resource(resource)44        except KeyError as exc:45            raise FileNotFoundError(exc.args[0])46 47    def is_resource(self, path):48        """49        Workaround for `zipfile.Path.is_file` returning true50        for non-existent paths.51        """52        target = self.files().joinpath(path)53        return target.is_file() and target.exists()54 55    def files(self):56        return ZipPath(self.archive, self.prefix)57 58 59class MultiplexedPath(abc.Traversable):60    """61    Given a series of Traversable objects, implement a merged62    version of the interface across all objects. Useful for63    namespace packages which may be multihomed at a single64    name.65    """66 67    def __init__(self, *paths):68        self._paths = list(map(_ensure_traversable, remove_duplicates(paths)))69        if not self._paths:70            message = 'MultiplexedPath must contain at least one path'71            raise FileNotFoundError(message)72        if not all(path.is_dir() for path in self._paths):73            raise NotADirectoryError('MultiplexedPath only supports directories')74 75    def iterdir(self):76        children = (child for path in self._paths for child in path.iterdir())77        by_name = operator.attrgetter('name')78        groups = itertools.groupby(sorted(children, key=by_name), key=by_name)79        return map(self._follow, (locs for name, locs in groups))80 81    def read_bytes(self):82        raise FileNotFoundError(f'{self} is not a file')83 84    def read_text(self, *args, **kwargs):85        raise FileNotFoundError(f'{self} is not a file')86 87    def is_dir(self):88        return True89 90    def is_file(self):91        return False92 93    def joinpath(self, *descendants):94        try:95            return super().joinpath(*descendants)96        except abc.TraversalError:97            # One of the paths did not resolve (a directory does not exist).98            # Just return something that will not exist.99            return self._paths[0].joinpath(*descendants)100 101    @classmethod102    def _follow(cls, children):103        """104        Construct a MultiplexedPath if needed.105 106        If children contains a sole element, return it.107        Otherwise, return a MultiplexedPath of the items.108        Unless one of the items is not a Directory, then return the first.109        """110        subdirs, one_dir, one_file = itertools.tee(children, 3)111 112        try:113            return only(one_dir)114        except ValueError:115            try:116                return cls(*subdirs)117            except NotADirectoryError:118                return next(one_file)119 120    def open(self, *args, **kwargs):121        raise FileNotFoundError(f'{self} is not a file')122 123    @property124    def name(self):125        return self._paths[0].name126 127    def __repr__(self):128        paths = ', '.join(f"'{path}'" for path in self._paths)129        return f'MultiplexedPath({paths})'130 131 132class NamespaceReader(abc.TraversableResources):133    def __init__(self, namespace_path):134        if 'NamespacePath' not in str(namespace_path):135            raise ValueError('Invalid path')136        self.path = MultiplexedPath(*map(self._resolve, namespace_path))137 138    @classmethod139    def _resolve(cls, path_str) -> abc.Traversable:140        r"""141        Given an item from a namespace path, resolve it to a Traversable.142 143        path_str might be a directory on the filesystem or a path to a144        zipfile plus the path within the zipfile, e.g. ``/foo/bar`` or145        ``/foo/baz.zip/inner_dir`` or ``foo\baz.zip\inner_dir\sub``.146        """147        (dir,) = (cand for cand in cls._candidate_paths(path_str) if cand.is_dir())148        return dir149 150    @classmethod151    def _candidate_paths(cls, path_str):152        yield pathlib.Path(path_str)153        yield from cls._resolve_zip_path(path_str)154 155    @staticmethod156    def _resolve_zip_path(path_str):157        for match in reversed(list(re.finditer(r'[\\/]', path_str))):158            with contextlib.suppress(159                FileNotFoundError,160                IsADirectoryError,161                NotADirectoryError,162                PermissionError,163            ):164                inner = path_str[match.end() :].replace('\\', '/') + '/'165                yield ZipPath(path_str[: match.start()], inner.lstrip('/'))166 167    def resource_path(self, resource):168        """169        Return the file system path to prevent170        `resources.path()` from creating a temporary171        copy.172        """173        return str(self.path.joinpath(resource))174 175    def files(self):176        return self.path177 178 179def _ensure_traversable(path):180    """181    Convert deprecated string arguments to traversables (pathlib.Path).182 183    Remove with Python 3.15.184    """185    if not isinstance(path, str):186        return path187 188    warnings.warn(189        "String arguments are deprecated. Pass a Traversable instead.",190        DeprecationWarning,191        stacklevel=3,192    )193 194    return pathlib.Path(path)195 
codekingpro/portable-devtools · Team Ai