sitecustomize.pyx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import pathlib
  2. import io
  3. import os
  4. import re
  5. import sys
  6. import warnings
  7. from importlib.metadata import Distribution, DistributionFinder, PackageNotFoundError, Prepared
  8. from importlib.resources.abc import Traversable
  9. import __res
  10. with warnings.catch_warnings(action="ignore", category=DeprecationWarning):
  11. from importlib.abc import ResourceReader
  12. ResourceReader.register(__res._ResfsResourceReader)
  13. METADATA_NAME = re.compile('^Name: (.*)$', re.MULTILINE)
  14. class ArcadiaResourceHandle(Traversable):
  15. def __init__(self, key):
  16. self.resfs_key = key
  17. def is_file(self):
  18. return True
  19. def is_dir(self):
  20. return False
  21. def open(self, mode='r', *args, **kwargs):
  22. data = __res.find(self.resfs_key.encode("utf-8"))
  23. if data is None:
  24. raise FileNotFoundError(self.resfs_key)
  25. stream = io.BytesIO(data)
  26. if 'b' not in mode:
  27. stream = io.TextIOWrapper(stream, *args, **kwargs)
  28. return stream
  29. def joinpath(self, *name):
  30. raise RuntimeError("Cannot traverse into a resource")
  31. def iterdir(self):
  32. return iter(())
  33. @property
  34. def name(self):
  35. return os.path.basename(self.resfs_key)
  36. class ArcadiaResourceContainer(Traversable):
  37. def __init__(self, prefix):
  38. self.resfs_prefix = prefix
  39. def is_dir(self):
  40. return True
  41. def is_file(self):
  42. return False
  43. def iterdir(self):
  44. for key, path_without_prefix in __res.iter_keys(self.resfs_prefix.encode("utf-8")):
  45. if b"/" in path_without_prefix:
  46. name = path_without_prefix.decode("utf-8").split("/", maxsplit=1)[0]
  47. yield ArcadiaResourceContainer(f"{self.resfs_prefix}{name}/")
  48. else:
  49. yield ArcadiaResourceHandle(key.decode("utf-8"))
  50. def open(self, *args, **kwargs):
  51. raise IsADirectoryError(self.resfs_prefix)
  52. def joinpath(self, *descendants):
  53. if not descendants:
  54. return self
  55. return ArcadiaResourceHandle(os.path.join(self.resfs_prefix, *descendants))
  56. @property
  57. def name(self):
  58. return os.path.basename(self.resfs_prefix[:-1])
  59. class ArcadiaDistribution(Distribution):
  60. def __init__(self, prefix):
  61. self.prefix = prefix
  62. @property
  63. def _path(self):
  64. return pathlib.Path(self.prefix)
  65. def read_text(self, filename):
  66. data = __res.resfs_read(f'{self.prefix}{filename}')
  67. if data:
  68. return data.decode('utf-8')
  69. read_text.__doc__ = Distribution.read_text.__doc__
  70. def locate_file(self, path):
  71. return f'{self.prefix}{path}'
  72. class ArcadiaMetadataFinder(DistributionFinder):
  73. prefixes = {}
  74. @classmethod
  75. def find_distributions(cls, context=DistributionFinder.Context()):
  76. found = cls._search_prefixes(context.name)
  77. return map(ArcadiaDistribution, found)
  78. @classmethod
  79. def _init_prefixes(cls):
  80. cls.prefixes.clear()
  81. for resource in __res.resfs_files():
  82. resource = resource.decode('utf-8')
  83. if not resource.endswith('METADATA'):
  84. continue
  85. data = __res.resfs_read(resource).decode('utf-8')
  86. metadata_name = METADATA_NAME.search(data)
  87. if metadata_name:
  88. metadata_name = Prepared(metadata_name.group(1))
  89. cls.prefixes[metadata_name.normalized] = resource[:-len('METADATA')]
  90. @classmethod
  91. def _search_prefixes(cls, name):
  92. if not cls.prefixes:
  93. cls._init_prefixes()
  94. if name:
  95. try:
  96. yield cls.prefixes[Prepared(name).normalized]
  97. except KeyError:
  98. raise PackageNotFoundError(name)
  99. else:
  100. for prefix in sorted(cls.prefixes.values()):
  101. yield prefix
  102. # monkeypatch standart library
  103. import importlib.metadata
  104. importlib.metadata.MetadataPathFinder = ArcadiaMetadataFinder