Coverage for /home/runner/work/coxbuild/coxbuild/src/coxbuild/extensions/gallery.py: 0%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import os
2from abc import ABC, abstractmethod
3from asyncio.log import logger
4from pathlib import Path
6from . import Extension
9class ExtensionGallery(ABC):
10 """Extension gallery."""
11 @abstractmethod
12 def load(self, name: str, version: str = "", hashcode: str = "") -> Extension | None:
13 pass
16class UriExtensionGallery(ExtensionGallery):
17 """Extension gallery by uri."""
18 @abstractmethod
19 def geturi(self, name: str, version: str = "", hashcode: str = "") -> str | None:
20 pass
22 def load(self, name: str, version: str = "", hashcode: str = "") -> Extension | None:
23 from .loader import load as loadext
25 try:
26 uri = self.geturi(name, version, hashcode)
27 if not uri:
28 return None
29 ext = loadext(uri)
30 sversion = f"@{version}" if version else ""
31 shash = f"@{ext.hashcode}" if ext.hashcode else ""
32 ext.uri = f"ext{shash}://{name}{sversion}"
33 return ext
34 except Exception as ex:
35 logger.warning(
36 f"Failed to load extension from gallery {self}: {ex}")
37 return None
40class UrlExtensionGallery(UriExtensionGallery):
41 """Extension gallery by url."""
42 @abstractmethod
43 def geturl(self, name: str, version: str = "", hashcode: str = "") -> str | None:
44 pass
46 def geturi(self, name: str, version: str = "", hashcode: str = "") -> str | None:
47 url = self.geturl(name, version, hashcode)
48 if not url:
49 return None
50 return f"url@{hashcode}://{url}" if hashcode else f"url://{url}"
53class DirectoryExtensionGallery(UriExtensionGallery):
54 """Extension gallery by directory."""
56 def __init__(self, path: Path):
57 self.path = path.resolve()
59 def geturi(self, name: str, version: str = "", hashcode: str = "") -> str | None:
60 sub = str(self.path.joinpath(f"{name}.py").resolve())
61 return f"file@{hashcode}://{sub}" if hashcode else f"file://{sub}"
64class GitHubExtensionGallery(UrlExtensionGallery):
65 """Extension gallery by GitHub repository."""
67 def __init__(self, repo: str = "StardustDL/coxbuild-ext-gallery", defaultVersion: str = "main") -> None:
68 super().__init__()
69 self.repo = repo
70 self.defaultVersion = defaultVersion
72 def geturl(self, name: str, version: str = "", hashcode: str = "") -> str | None:
73 if not version:
74 version = self.defaultVersion
75 return f"https://raw.githubusercontent.com/{self.repo}/{version}/exts/{name}.py"
78class JsDelivrExtensionGallery(UrlExtensionGallery):
79 """Extension gallery by GitHub repository on jsDelivr CDN."""
81 def __init__(self, repo: str = "StardustDL/coxbuild-ext-gallery", defaultVersion: str = "main") -> None:
82 super().__init__()
83 self.repo = repo
84 self.defaultVersion = defaultVersion
86 def geturl(self, name: str, version: str = "", hashcode: str = "") -> str | None:
87 if not version:
88 version = self.defaultVersion
89 return f"https://cdn.jsdelivr.net/gh/{self.repo}@{version}/exts/{name}.py"
92class GiteeExtensionGallery(UrlExtensionGallery):
93 """Extension gallery by Gitee repository."""
95 def __init__(self, repo: str = "stardustdl/coxbuild-ext-gallery", defaultVersion: str = "main") -> None:
96 super().__init__()
97 self.repo = repo
98 self.defaultVersion = defaultVersion
100 def geturl(self, name: str, version: str = "", hashcode: str = "") -> str | None:
101 if not version:
102 version = self.defaultVersion
103 return f"https://gitee.com/{self.repo}/raw/{version}/exts/{name}.py"
106_galleries: list[ExtensionGallery] = []
109def galleries():
110 """
111 Get extension galleries.
113 From COXBUILD_GALLERY environment variable.
114 GitHub provider: github@reponame
115 Gitee provider: gitee@reponame
116 Directory provider: directory@directory/path
117 """
118 if _galleries:
119 return _galleries
120 env = os.getenv("COXBUILD_GALLERY", "").split(";")
122 for item in env:
123 item = item.strip()
124 if not item:
125 continue
127 try:
128 entry = item.split("@", 1)
129 if len(entry) != 2:
130 type, name = "directory", entry[0]
131 else:
132 type, name = entry
134 match type.lower():
135 case "directory":
136 _galleries.append(DirectoryExtensionGallery(Path(name)))
137 case "github":
138 _galleries.append(JsDelivrExtensionGallery(name))
139 _galleries.append(GitHubExtensionGallery(name))
140 case "gitee":
141 _galleries.append(GiteeExtensionGallery(name))
142 except:
143 logger.error(f"Failed to add extension gallery: {item}")
145 _galleries.append(GiteeExtensionGallery())
146 _galleries.append(JsDelivrExtensionGallery())
147 _galleries.append(GitHubExtensionGallery())
149 return _galleries