|
1 | 1 | import pathlib |
2 | 2 |
|
3 | 3 | import pytest |
| 4 | +import resolvelib |
4 | 5 | from packaging.requirements import Requirement |
| 6 | +from packaging.version import Version |
5 | 7 |
|
6 | | -from fromager import context, finders |
| 8 | +from fromager import constraints, context, finders |
7 | 9 |
|
8 | 10 |
|
9 | 11 | @pytest.mark.parametrize( |
@@ -110,3 +112,161 @@ def test_find_source_dir( |
110 | 112 | req = Requirement(dist_name) |
111 | 113 | actual = finders.find_source_dir(tmp_context, work_dir, req, version_string) |
112 | 114 | assert str(source_dir) == str(actual) |
| 115 | + |
| 116 | + |
| 117 | +# -- LocalIndexProvider ------------------------------------------------------ |
| 118 | + |
| 119 | + |
| 120 | +def _create_local_file(directory: pathlib.Path, filename: str) -> pathlib.Path: |
| 121 | + """Create an empty file in the given directory.""" |
| 122 | + path = directory / filename |
| 123 | + path.touch() |
| 124 | + return path |
| 125 | + |
| 126 | + |
| 127 | +def test_local_init(tmp_path: pathlib.Path) -> None: |
| 128 | + provider = finders.LocalIndexProvider(path=tmp_path, flat=True) |
| 129 | + assert provider.path == tmp_path |
| 130 | + assert provider.flat is True |
| 131 | + assert provider.include_sdists is True |
| 132 | + assert provider.include_wheels is True |
| 133 | + assert provider.supports_upload_time is False |
| 134 | + assert provider.use_cache_candidates is False |
| 135 | + assert provider.cooldown is None |
| 136 | + |
| 137 | + with pytest.raises(NotImplementedError): |
| 138 | + _ = provider.cache_key |
| 139 | + |
| 140 | + desc = provider.get_provider_description() |
| 141 | + assert "Local" in desc |
| 142 | + assert str(tmp_path) in desc |
| 143 | + |
| 144 | + |
| 145 | +def test_local_find_candidates_flat(tmp_path: pathlib.Path) -> None: |
| 146 | + """Flat mode finds wheels, sdists, and build-tagged wheels in one directory.""" |
| 147 | + # wheel and sdist with same version |
| 148 | + _create_local_file(tmp_path, "example_pkg-1.0.0-py3-none-any.whl") |
| 149 | + _create_local_file(tmp_path, "example_pkg-1.0.0.tar.gz") |
| 150 | + # sdist filenames can use non-normalized names (dash, underscore, dot) |
| 151 | + _create_local_file(tmp_path, "example-pkg-2.0.0.tar.gz") |
| 152 | + _create_local_file(tmp_path, "example.pkg-3.0.0.tar.gz") |
| 153 | + _create_local_file(tmp_path, "example_pkg-4.0.0-2-py3-none-any.whl") |
| 154 | + # noise: other package, directory shaped like a wheel, non-dist file, invalid sdist |
| 155 | + _create_local_file(tmp_path, "other_pkg-1.0.0-py3-none-any.whl") |
| 156 | + tmp_path.joinpath("example_pkg-9.0.0-py3-none-any.whl").mkdir() |
| 157 | + _create_local_file(tmp_path, "README.md") |
| 158 | + _create_local_file(tmp_path, "not-a-valid-sdist.tar.gz") |
| 159 | + |
| 160 | + provider = finders.LocalIndexProvider(path=tmp_path, flat=True) |
| 161 | + candidates = list(provider.find_candidates("example-pkg")) |
| 162 | + |
| 163 | + # find_candidates filters by normalized name; other_pkg is excluded |
| 164 | + assert len(candidates) == 5 |
| 165 | + assert all(c.name == "example-pkg" for c in candidates) |
| 166 | + |
| 167 | + # version 1.0.0 has both a wheel and an sdist |
| 168 | + v1 = [c for c in candidates if c.version == Version("1.0.0")] |
| 169 | + assert len(v1) == 2 |
| 170 | + assert {c.is_sdist for c in v1} == {True, False} |
| 171 | + whl = next(c for c in v1 if not c.is_sdist) |
| 172 | + assert whl.upload_time is None |
| 173 | + assert whl.has_metadata is False |
| 174 | + |
| 175 | + # all sdist name variants normalize to example-pkg |
| 176 | + sdists = [c for c in candidates if c.is_sdist] |
| 177 | + assert {str(c.version) for c in sdists} == {"1.0.0", "2.0.0", "3.0.0"} |
| 178 | + |
| 179 | + assert next(c for c in candidates if c.version == Version("4.0.0")).build_tag == ( |
| 180 | + 2, |
| 181 | + "", |
| 182 | + ) |
| 183 | + |
| 184 | + |
| 185 | +def test_local_find_candidates_nested(tmp_path: pathlib.Path) -> None: |
| 186 | + """Nested mode looks up a subdirectory using the canonicalized name.""" |
| 187 | + pkg_dir = tmp_path / "my-package" |
| 188 | + pkg_dir.mkdir() |
| 189 | + _create_local_file(pkg_dir, "my_package-1.0.0-py3-none-any.whl") |
| 190 | + |
| 191 | + provider = finders.LocalIndexProvider(path=tmp_path, flat=False) |
| 192 | + |
| 193 | + # Various spellings should all resolve via canonicalize_name |
| 194 | + for name in ("My_Package", "my.package", "MY-PACKAGE", "my-package"): |
| 195 | + candidates = list(provider.find_candidates(name)) |
| 196 | + assert len(candidates) == 1, f"failed for {name!r}" |
| 197 | + assert candidates[0].name == "my-package" |
| 198 | + assert candidates[0].version == Version("1.0.0") |
| 199 | + |
| 200 | + # missing subdirectory returns no candidates |
| 201 | + assert list(provider.find_candidates("no-such-pkg")) == [] |
| 202 | + |
| 203 | + |
| 204 | +def test_local_find_candidates_include_flags(tmp_path: pathlib.Path) -> None: |
| 205 | + """include_sdists=False / include_wheels=False filters correctly.""" |
| 206 | + _create_local_file(tmp_path, "example_pkg-1.0.0.tar.gz") |
| 207 | + _create_local_file(tmp_path, "example_pkg-1.0.0-py3-none-any.whl") |
| 208 | + |
| 209 | + wheels_only = finders.LocalIndexProvider( |
| 210 | + path=tmp_path, flat=True, include_sdists=False |
| 211 | + ) |
| 212 | + assert all(not c.is_sdist for c in wheels_only.find_candidates("example-pkg")) |
| 213 | + |
| 214 | + sdists_only = finders.LocalIndexProvider( |
| 215 | + path=tmp_path, flat=True, include_wheels=False |
| 216 | + ) |
| 217 | + assert all(c.is_sdist for c in sdists_only.find_candidates("example-pkg")) |
| 218 | + |
| 219 | + |
| 220 | +def test_local_find_candidates_empty_dir(tmp_path: pathlib.Path) -> None: |
| 221 | + provider = finders.LocalIndexProvider(path=tmp_path, flat=True) |
| 222 | + assert list(provider.find_candidates("nonexistent-pkg")) == [] |
| 223 | + |
| 224 | + |
| 225 | +def test_local_find_matches(tmp_path: pathlib.Path) -> None: |
| 226 | + """find_matches sorts by version descending and respects constraints.""" |
| 227 | + _create_local_file(tmp_path, "example_pkg-1.0.0-py3-none-any.whl") |
| 228 | + _create_local_file(tmp_path, "example_pkg-1.5.0-py3-none-any.whl") |
| 229 | + _create_local_file(tmp_path, "example_pkg-2.0.0-py3-none-any.whl") |
| 230 | + _create_local_file(tmp_path, "other_pkg-5.0.0-py3-none-any.whl") |
| 231 | + |
| 232 | + provider = finders.LocalIndexProvider(path=tmp_path, flat=True) |
| 233 | + req = Requirement("example-pkg") |
| 234 | + identifier = provider.identify(req) |
| 235 | + matches = list( |
| 236 | + provider.find_matches( |
| 237 | + identifier=identifier, |
| 238 | + requirements={identifier: [req]}, |
| 239 | + incompatibilities={}, |
| 240 | + ) |
| 241 | + ) |
| 242 | + # other_pkg is excluded by name; results sorted highest first |
| 243 | + assert [m.version for m in matches] == [ |
| 244 | + Version("2.0.0"), |
| 245 | + Version("1.5.0"), |
| 246 | + Version("1.0.0"), |
| 247 | + ] |
| 248 | + |
| 249 | + # constraint filters out 2.0.0 |
| 250 | + c = constraints.Constraints() |
| 251 | + c.add_constraint("example-pkg<2") |
| 252 | + provider_c = finders.LocalIndexProvider(path=tmp_path, flat=True, constraints=c) |
| 253 | + matches_c = list( |
| 254 | + provider_c.find_matches( |
| 255 | + identifier=identifier, |
| 256 | + requirements={identifier: [req]}, |
| 257 | + incompatibilities={}, |
| 258 | + ) |
| 259 | + ) |
| 260 | + assert [m.version for m in matches_c] == [Version("1.5.0"), Version("1.0.0")] |
| 261 | + |
| 262 | + # no match raises |
| 263 | + provider_miss = finders.LocalIndexProvider(path=tmp_path, flat=True) |
| 264 | + req_miss = Requirement("example-pkg>=5.0") |
| 265 | + with pytest.raises(resolvelib.resolvers.ResolverException): |
| 266 | + list( |
| 267 | + provider_miss.find_matches( |
| 268 | + identifier=identifier, |
| 269 | + requirements={identifier: [req_miss]}, |
| 270 | + incompatibilities={}, |
| 271 | + ) |
| 272 | + ) |
0 commit comments