Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pip/_internal/utils/pylock.py: 22%

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

120 statements  

1from __future__ import annotations 

2 

3import os 

4import re 

5from collections.abc import Iterable, Iterator 

6from pathlib import Path 

7from typing import TYPE_CHECKING 

8from urllib.parse import urljoin, urlsplit 

9 

10from pip._vendor.packaging.pylock import ( 

11 Package, 

12 PackageArchive, 

13 PackageDirectory, 

14 PackageSdist, 

15 PackageVcs, 

16 PackageWheel, 

17 Pylock, 

18 is_valid_pylock_path, 

19) 

20from pip._vendor.packaging.version import Version 

21 

22from pip._internal.exceptions import InstallationError 

23from pip._internal.models.link import Link 

24from pip._internal.utils.compat import tomllib 

25from pip._internal.utils.urls import path_to_url, url_to_path 

26 

27if TYPE_CHECKING: 

28 from pip._internal.network.session import PipSession 

29 from pip._internal.req.req_install import InstallRequirement 

30 

31 

32def _pylock_package_from_install_requirement( 

33 ireq: InstallRequirement, base_dir: Path 

34) -> Package: 

35 base_dir = base_dir.resolve() 

36 dist = ireq.get_dist() 

37 download_info = ireq.download_info 

38 assert download_info 

39 package_version = None 

40 package_vcs = None 

41 package_directory = None 

42 package_archive = None 

43 package_sdist = None 

44 package_wheels = None 

45 if ireq.is_direct: 

46 if download_info.vcs_info: 

47 package_vcs = PackageVcs( 

48 type=download_info.vcs_info.vcs, 

49 url=download_info.url, 

50 path=None, 

51 requested_revision=download_info.vcs_info.requested_revision, 

52 commit_id=download_info.vcs_info.commit_id, 

53 subdirectory=download_info.subdirectory, 

54 ) 

55 elif download_info.dir_info: 

56 package_directory = PackageDirectory( 

57 path=( 

58 Path(url_to_path(download_info.url)) 

59 .resolve() 

60 .relative_to(base_dir) 

61 .as_posix() 

62 ), 

63 editable=( 

64 download_info.dir_info.editable 

65 if download_info.dir_info.editable 

66 else None 

67 ), 

68 subdirectory=download_info.subdirectory, 

69 ) 

70 elif download_info.archive_info: 

71 if not download_info.archive_info.hashes: 

72 raise NotImplementedError() 

73 package_archive = PackageArchive( 

74 url=download_info.url, 

75 path=None, 

76 hashes=download_info.archive_info.hashes, 

77 subdirectory=download_info.subdirectory, 

78 ) 

79 else: 

80 # should never happen 

81 raise NotImplementedError() 

82 else: 

83 package_version = dist.version 

84 if download_info.archive_info: 

85 if not download_info.archive_info.hashes: 

86 raise NotImplementedError() 

87 link = Link(download_info.url) 

88 if link.is_wheel: 

89 package_wheels = [ 

90 PackageWheel( 

91 name=link.filename, 

92 url=download_info.url, 

93 hashes=download_info.archive_info.hashes, 

94 ) 

95 ] 

96 else: 

97 package_sdist = PackageSdist( 

98 name=link.filename, 

99 url=download_info.url, 

100 hashes=download_info.archive_info.hashes, 

101 ) 

102 else: 

103 # should never happen 

104 raise NotImplementedError() 

105 return Package( 

106 name=dist.canonical_name, 

107 version=package_version, 

108 vcs=package_vcs, 

109 directory=package_directory, 

110 archive=package_archive, 

111 sdist=package_sdist, 

112 wheels=package_wheels, 

113 ) 

114 

115 

116def pylock_from_install_requirements( 

117 install_requirements: Iterable[InstallRequirement], base_dir: Path 

118) -> Pylock: 

119 return Pylock( 

120 lock_version=Version("1.0"), 

121 created_by="pip", 

122 packages=sorted( 

123 ( 

124 _pylock_package_from_install_requirement(ireq, base_dir) 

125 for ireq in install_requirements 

126 ), 

127 key=lambda p: p.name, 

128 ), 

129 ) 

130 

131 

132_SCHEME_RE = re.compile("^(http|https|file)://", re.IGNORECASE) 

133 

134 

135def _is_url(s: str) -> bool: 

136 return bool(_SCHEME_RE.match(s)) 

137 

138 

139def is_valid_pylock_filename(filename: str) -> bool: 

140 if _is_url(filename): 

141 path = Path(urlsplit(filename).path.rpartition("/")[-1]) 

142 else: 

143 path = Path(filename) 

144 return is_valid_pylock_path(path) 

145 

146 

147def _package_dist_url( 

148 pylock_path_or_url: str, path: str | None, url: str | None 

149) -> str: 

150 """Compute an url from a Pylock package path and url. 

151 

152 Give priority to path over url. If path is relative, 

153 compute an url using the pylock file location as base. 

154 """ 

155 if path is not None: 

156 if not os.path.isabs(path): 

157 # relative path, join to pylock location 

158 if _is_url(pylock_path_or_url): 

159 return urljoin(pylock_path_or_url, path) 

160 else: 

161 return path_to_url( 

162 os.path.join(os.path.dirname(pylock_path_or_url), path) 

163 ) 

164 else: 

165 # absolute path, reject if pylock comes from a URL 

166 if _is_url(pylock_path_or_url): 

167 raise InstallationError( 

168 f"Absolute paths are not supported in pylock files obtained " 

169 f"from a URL: {path!r} in {pylock_path_or_url!r}" 

170 ) 

171 return path_to_url(path) 

172 else: 

173 assert url is not None # guaranteed by packaging.pylock validation 

174 return url 

175 

176 

177def package_vcs_requirement_url( 

178 pylock_path_or_url: str, package_vcs: PackageVcs 

179) -> str: 

180 dist_url = _package_dist_url(pylock_path_or_url, package_vcs.path, package_vcs.url) 

181 url = f"{package_vcs.type}+{dist_url}@{package_vcs.commit_id}" 

182 if package_vcs.subdirectory: 

183 if "#" in url: 

184 raise InstallationError( 

185 f"Package URL {url!r} cannot contain fragments in combination " 

186 f"with subdirectory field (in {pylock_path_or_url!r})" 

187 ) 

188 url += "#subdirectory=" + package_vcs.subdirectory 

189 return url 

190 

191 

192def package_archive_requirement_url( 

193 pylock_path_or_url: str, package_archive: PackageArchive 

194) -> str: 

195 url = _package_dist_url( 

196 pylock_path_or_url, package_archive.path, package_archive.url 

197 ) 

198 if package_archive.subdirectory: 

199 if "#" in url: 

200 raise InstallationError( 

201 f"Package URL {url!r} cannot contain fragments in combination " 

202 f"with subdirectory field (in {pylock_path_or_url!r})" 

203 ) 

204 url += "#subdirectory=" + package_archive.subdirectory 

205 return url 

206 

207 

208def package_directory_requirement_url( 

209 pylock_path_or_url: str, package_directory: PackageDirectory 

210) -> str: 

211 if _is_url(pylock_path_or_url) and not pylock_path_or_url.startswith("file://"): 

212 raise InstallationError( 

213 f"Directory entries are not supported in remote pylock.toml " 

214 f"{pylock_path_or_url!r}" 

215 ) 

216 url = _package_dist_url(pylock_path_or_url, package_directory.path, None) 

217 assert url.startswith("file://") 

218 if not url.endswith("/"): 

219 url += "/" 

220 if package_directory.subdirectory: 

221 url += package_directory.subdirectory 

222 if not url.endswith("/"): 

223 url += "/" 

224 return url 

225 

226 

227def package_sdist_requirement_url( 

228 pylock_path_or_url: str, package_sdist: PackageSdist 

229) -> str: 

230 return _package_dist_url(pylock_path_or_url, package_sdist.path, package_sdist.url) 

231 

232 

233def package_wheel_requirement_url( 

234 pylock_path_or_url: str, package_wheel: PackageWheel 

235) -> str: 

236 return _package_dist_url(pylock_path_or_url, package_wheel.path, package_wheel.url) 

237 

238 

239def _get_pylock_path_or_url_content(path_or_url: str, session: PipSession) -> str: 

240 # TODO: refactor - this is similar to req_file.get_file_content 

241 scheme = urlsplit(path_or_url).scheme 

242 # Pip has special support for file:// URLs (LocalFSAdapter). 

243 if scheme in ["http", "https", "file"]: 

244 # Delay importing heavy network modules until absolutely necessary. 

245 from pip._internal.network.utils import raise_for_status 

246 

247 resp = session.get(path_or_url) 

248 raise_for_status(resp) 

249 return resp.text 

250 

251 # Assume this is a bare path. 

252 return Path(path_or_url).read_text(encoding="utf-8") 

253 

254 

255def select_from_pylock_path_or_url( 

256 pylock_path_or_url: str, 

257 session: PipSession, 

258) -> Iterator[ 

259 tuple[ 

260 Package, 

261 PackageVcs | PackageDirectory | PackageArchive | PackageWheel | PackageSdist, 

262 ] 

263]: 

264 try: 

265 pylock_content = _get_pylock_path_or_url_content(pylock_path_or_url, session) 

266 except Exception as exc: 

267 raise InstallationError( 

268 f"Error reading pylock file {pylock_path_or_url!r}: {exc}" 

269 ) from exc 

270 

271 try: 

272 lock = Pylock.from_dict(tomllib.loads(pylock_content)) 

273 except Exception as exc: 

274 raise InstallationError( 

275 f"Invalid pylock file {pylock_path_or_url!r}: {exc}" 

276 ) from exc 

277 

278 try: 

279 yield from lock.select() 

280 except Exception as exc: 

281 raise InstallationError( 

282 f"Cannot select requirements from pylock file {pylock_path_or_url!r}: {exc}" 

283 ) from exc