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

97 statements  

1import os 

2from abc import ABC, abstractmethod 

3from asyncio.log import logger 

4from pathlib import Path 

5 

6from . import Extension 

7 

8 

9class ExtensionGallery(ABC): 

10 """Extension gallery.""" 

11 @abstractmethod 

12 def load(self, name: str, version: str = "", hashcode: str = "") -> Extension | None: 

13 pass 

14 

15 

16class UriExtensionGallery(ExtensionGallery): 

17 """Extension gallery by uri.""" 

18 @abstractmethod 

19 def geturi(self, name: str, version: str = "", hashcode: str = "") -> str | None: 

20 pass 

21 

22 def load(self, name: str, version: str = "", hashcode: str = "") -> Extension | None: 

23 from .loader import load as loadext 

24 

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 

38 

39 

40class UrlExtensionGallery(UriExtensionGallery): 

41 """Extension gallery by url.""" 

42 @abstractmethod 

43 def geturl(self, name: str, version: str = "", hashcode: str = "") -> str | None: 

44 pass 

45 

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}" 

51 

52 

53class DirectoryExtensionGallery(UriExtensionGallery): 

54 """Extension gallery by directory.""" 

55 

56 def __init__(self, path: Path): 

57 self.path = path.resolve() 

58 

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}" 

62 

63 

64class GitHubExtensionGallery(UrlExtensionGallery): 

65 """Extension gallery by GitHub repository.""" 

66 

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 

71 

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" 

76 

77 

78class JsDelivrExtensionGallery(UrlExtensionGallery): 

79 """Extension gallery by GitHub repository on jsDelivr CDN.""" 

80 

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 

85 

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" 

90 

91 

92class GiteeExtensionGallery(UrlExtensionGallery): 

93 """Extension gallery by Gitee repository.""" 

94 

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 

99 

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" 

104 

105 

106_galleries: list[ExtensionGallery] = [] 

107 

108 

109def galleries(): 

110 """ 

111 Get extension galleries. 

112 

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(";") 

121 

122 for item in env: 

123 item = item.strip() 

124 if not item: 

125 continue 

126 

127 try: 

128 entry = item.split("@", 1) 

129 if len(entry) != 2: 

130 type, name = "directory", entry[0] 

131 else: 

132 type, name = entry 

133 

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}") 

144 

145 _galleries.append(GiteeExtensionGallery()) 

146 _galleries.append(JsDelivrExtensionGallery()) 

147 _galleries.append(GitHubExtensionGallery()) 

148 

149 return _galleries