simple.py 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. """
  2. Interface adapters for low-level readers.
  3. """
  4. import abc
  5. import io
  6. import itertools
  7. from typing import BinaryIO, List
  8. from .abc import Traversable, TraversableResources
  9. class SimpleReader(abc.ABC):
  10. """
  11. The minimum, low-level interface required from a resource
  12. provider.
  13. """
  14. @property
  15. @abc.abstractmethod
  16. def package(self) -> str:
  17. """
  18. The name of the package for which this reader loads resources.
  19. """
  20. @abc.abstractmethod
  21. def children(self) -> List['SimpleReader']:
  22. """
  23. Obtain an iterable of SimpleReader for available
  24. child containers (e.g. directories).
  25. """
  26. @abc.abstractmethod
  27. def resources(self) -> List[str]:
  28. """
  29. Obtain available named resources for this virtual package.
  30. """
  31. @abc.abstractmethod
  32. def open_binary(self, resource: str) -> BinaryIO:
  33. """
  34. Obtain a File-like for a named resource.
  35. """
  36. @property
  37. def name(self):
  38. return self.package.split('.')[-1]
  39. class ResourceContainer(Traversable):
  40. """
  41. Traversable container for a package's resources via its reader.
  42. """
  43. def __init__(self, reader: SimpleReader):
  44. self.reader = reader
  45. def is_dir(self):
  46. return True
  47. def is_file(self):
  48. return False
  49. def iterdir(self):
  50. files = (ResourceHandle(self, name) for name in self.reader.resources)
  51. dirs = map(ResourceContainer, self.reader.children())
  52. return itertools.chain(files, dirs)
  53. def open(self, *args, **kwargs):
  54. raise IsADirectoryError()
  55. class ResourceHandle(Traversable):
  56. """
  57. Handle to a named resource in a ResourceReader.
  58. """
  59. def __init__(self, parent: ResourceContainer, name: str):
  60. self.parent = parent
  61. self.name = name # type: ignore
  62. def is_file(self):
  63. return True
  64. def is_dir(self):
  65. return False
  66. def open(self, mode='r', *args, **kwargs):
  67. stream = self.parent.reader.open_binary(self.name)
  68. if 'b' not in mode:
  69. stream = io.TextIOWrapper(*args, **kwargs)
  70. return stream
  71. def joinpath(self, name):
  72. raise RuntimeError("Cannot traverse into a resource")
  73. class TraversableReader(TraversableResources, SimpleReader):
  74. """
  75. A TraversableResources based on SimpleReader. Resource providers
  76. may derive from this class to provide the TraversableResources
  77. interface by supplying the SimpleReader interface.
  78. """
  79. def files(self):
  80. return ResourceContainer(self)