py-rattler 0.22.0__cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. py_rattler-0.22.0.dist-info/METADATA +208 -0
  2. py_rattler-0.22.0.dist-info/RECORD +68 -0
  3. py_rattler-0.22.0.dist-info/WHEEL +4 -0
  4. rattler/__init__.py +114 -0
  5. rattler/channel/__init__.py +5 -0
  6. rattler/channel/channel.py +94 -0
  7. rattler/channel/channel_config.py +43 -0
  8. rattler/channel/channel_priority.py +14 -0
  9. rattler/exceptions.py +120 -0
  10. rattler/explicit_environment/__init__.py +3 -0
  11. rattler/explicit_environment/environment.py +69 -0
  12. rattler/index/__init__.py +3 -0
  13. rattler/index/index.py +112 -0
  14. rattler/install/__init__.py +3 -0
  15. rattler/install/installer.py +96 -0
  16. rattler/lock/__init__.py +23 -0
  17. rattler/lock/channel.py +52 -0
  18. rattler/lock/environment.py +213 -0
  19. rattler/lock/hash.py +33 -0
  20. rattler/lock/lock_file.py +118 -0
  21. rattler/lock/package.py +302 -0
  22. rattler/match_spec/__init__.py +4 -0
  23. rattler/match_spec/match_spec.py +294 -0
  24. rattler/match_spec/nameless_match_spec.py +185 -0
  25. rattler/networking/__init__.py +21 -0
  26. rattler/networking/client.py +74 -0
  27. rattler/networking/fetch_repo_data.py +103 -0
  28. rattler/networking/middleware.py +234 -0
  29. rattler/package/__init__.py +26 -0
  30. rattler/package/about_json.py +329 -0
  31. rattler/package/index_json.py +437 -0
  32. rattler/package/no_arch_type.py +142 -0
  33. rattler/package/package_name.py +204 -0
  34. rattler/package/package_name_matcher.py +81 -0
  35. rattler/package/paths_json.py +696 -0
  36. rattler/package/run_exports_json.py +268 -0
  37. rattler/package_streaming/__init__.py +26 -0
  38. rattler/platform/__init__.py +4 -0
  39. rattler/platform/arch.py +59 -0
  40. rattler/platform/platform.py +217 -0
  41. rattler/prefix/__init__.py +4 -0
  42. rattler/prefix/prefix_paths.py +442 -0
  43. rattler/prefix/prefix_record.py +234 -0
  44. rattler/pty/__init__.py +25 -0
  45. rattler/pty/pty_process.py +391 -0
  46. rattler/pty/pty_session.py +241 -0
  47. rattler/py.typed +0 -0
  48. rattler/rattler.abi3.so +0 -0
  49. rattler/repo_data/__init__.py +19 -0
  50. rattler/repo_data/gateway.py +337 -0
  51. rattler/repo_data/package_record.py +938 -0
  52. rattler/repo_data/patch_instructions.py +22 -0
  53. rattler/repo_data/record.py +164 -0
  54. rattler/repo_data/repo_data.py +74 -0
  55. rattler/repo_data/source.py +85 -0
  56. rattler/repo_data/sparse.py +356 -0
  57. rattler/shell/__init__.py +3 -0
  58. rattler/shell/shell.py +134 -0
  59. rattler/solver/__init__.py +3 -0
  60. rattler/solver/solver.py +220 -0
  61. rattler/utils/rattler_version.py +19 -0
  62. rattler/version/__init__.py +5 -0
  63. rattler/version/version.py +591 -0
  64. rattler/version/version_spec.py +184 -0
  65. rattler/version/with_source.py +80 -0
  66. rattler/virtual_package/__init__.py +4 -0
  67. rattler/virtual_package/generic.py +136 -0
  68. rattler/virtual_package/virtual_package.py +201 -0
@@ -0,0 +1,22 @@
1
+ from __future__ import annotations
2
+
3
+ from rattler.rattler import PyPatchInstructions
4
+
5
+
6
+ class PatchInstructions:
7
+ _patch_instructions: PyPatchInstructions
8
+
9
+ @classmethod
10
+ def _from_py_patch_instructions(cls, py_patch_instructions: PyPatchInstructions) -> PatchInstructions:
11
+ """
12
+ Construct Rattler PatchInstructions from FFI PyPatchInstructions object.
13
+ """
14
+ patch_instructions = cls.__new__(cls)
15
+ patch_instructions._patch_instructions = py_patch_instructions
16
+ return patch_instructions
17
+
18
+ def __repr__(self) -> str:
19
+ """
20
+ Returns a representation of the PatchInstructions.
21
+ """
22
+ return f"{type(self).__name__}()"
@@ -0,0 +1,164 @@
1
+ from __future__ import annotations
2
+
3
+ from rattler.rattler import PyRecord
4
+ from rattler.repo_data.package_record import PackageRecord
5
+
6
+
7
+ class RepoDataRecord(PackageRecord):
8
+ def __init__(self, package_record: PackageRecord, file_name: str, url: str, channel: str) -> None:
9
+ record = PyRecord.create_repodata_record(
10
+ package_record._record,
11
+ file_name,
12
+ url,
13
+ channel,
14
+ )
15
+ self._record = record
16
+
17
+ @property
18
+ def url(self) -> str:
19
+ """
20
+ The canonical URL from where to get this package.
21
+
22
+ Examples
23
+ --------
24
+ ```python
25
+ >>> from rattler import PrefixRecord
26
+ >>> record = PrefixRecord.from_path(
27
+ ... "../test-data/conda-meta/libsqlite-3.40.0-hcfcfb64_0.json"
28
+ ... )
29
+ >>> record.url
30
+ 'https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.40.0-hcfcfb64_0.tar.bz2'
31
+ >>>
32
+ ```
33
+ """
34
+ return self._record.url
35
+
36
+ @url.setter
37
+ def url(self, value: str) -> None:
38
+ """
39
+ Set the canonical URL for this package.
40
+
41
+ Examples
42
+ --------
43
+ ```python
44
+ >>> from rattler import PrefixRecord
45
+ >>> record = PrefixRecord.from_path(
46
+ ... "../test-data/conda-meta/libsqlite-3.40.0-hcfcfb64_0.json"
47
+ ... )
48
+ >>> record.url = "https://example.com/package.tar.bz2"
49
+ >>> record.url
50
+ 'https://example.com/package.tar.bz2'
51
+ >>>
52
+ ```
53
+ """
54
+ self._record.set_url(value)
55
+
56
+ @property
57
+ def channel(self) -> str:
58
+ """
59
+ String representation of the channel where the
60
+ package comes from. This could be a URL but it
61
+ could also be a channel name.
62
+
63
+ Examples
64
+ --------
65
+ ```python
66
+ >>> from rattler import PrefixRecord
67
+ >>> record = PrefixRecord.from_path(
68
+ ... "../test-data/conda-meta/libsqlite-3.40.0-hcfcfb64_0.json"
69
+ ... )
70
+ >>> record.channel
71
+ 'https://conda.anaconda.org/conda-forge/win-64'
72
+ >>>
73
+ ```
74
+ """
75
+ return self._record.channel
76
+
77
+ @channel.setter
78
+ def channel(self, value: str) -> None:
79
+ """
80
+ Set the channel for this package.
81
+
82
+ Examples
83
+ --------
84
+ ```python
85
+ >>> from rattler import PrefixRecord
86
+ >>> record = PrefixRecord.from_path(
87
+ ... "../test-data/conda-meta/libsqlite-3.40.0-hcfcfb64_0.json"
88
+ ... )
89
+ >>> record.channel = "conda-forge"
90
+ >>> record.channel
91
+ 'conda-forge'
92
+ >>>
93
+ ```
94
+ """
95
+ self._record.set_channel(value)
96
+
97
+ @property
98
+ def file_name(self) -> str:
99
+ """
100
+ The filename of the package.
101
+
102
+ Examples
103
+ --------
104
+ ```python
105
+ >>> from rattler import PrefixRecord
106
+ >>> record = PrefixRecord.from_path(
107
+ ... "../test-data/conda-meta/libsqlite-3.40.0-hcfcfb64_0.json"
108
+ ... )
109
+ >>> record.file_name
110
+ 'libsqlite-3.40.0-hcfcfb64_0.tar.bz2'
111
+ >>>
112
+ ```
113
+ """
114
+ return self._record.file_name
115
+
116
+ @file_name.setter
117
+ def file_name(self, value: str) -> None:
118
+ """
119
+ Set the filename of the package.
120
+
121
+ Examples
122
+ --------
123
+ ```python
124
+ >>> from rattler import PrefixRecord
125
+ >>> record = PrefixRecord.from_path(
126
+ ... "../test-data/conda-meta/libsqlite-3.40.0-hcfcfb64_0.json"
127
+ ... )
128
+ >>> record.file_name = "new-package-1.0.tar.bz2"
129
+ >>> record.file_name
130
+ 'new-package-1.0.tar.bz2'
131
+ >>>
132
+ ```
133
+ """
134
+ self._record.set_file_name(value)
135
+
136
+ @classmethod
137
+ def _from_py_record(cls, py_record: PyRecord) -> RepoDataRecord:
138
+ """
139
+ Construct Rattler RepoDataRecord from FFI PyRecord object.
140
+ """
141
+
142
+ # quick sanity check
143
+ assert py_record.is_repodata_record
144
+ record = cls.__new__(cls)
145
+ record._record = py_record
146
+ return record
147
+
148
+ def __repr__(self) -> str:
149
+ """
150
+ Returns a representation of the RepoDataRecord.
151
+
152
+ Examples
153
+ --------
154
+ ```python
155
+ >>> from rattler import RepoData, Channel
156
+ >>> repo_data = RepoData(
157
+ ... "../test-data/test-server/repo/noarch/repodata.json"
158
+ ... )
159
+ >>> repo_data.into_repo_data(Channel("test"))[0]
160
+ RepoDataRecord(url="https://conda.anaconda.org/test/noarch/test-package-0.1-0.tar.bz2")
161
+ >>>
162
+ ```
163
+ """
164
+ return f'{type(self).__name__}(url="{self.url}")'
@@ -0,0 +1,74 @@
1
+ from __future__ import annotations
2
+ from pathlib import Path
3
+ from typing import Union, List, TYPE_CHECKING
4
+
5
+
6
+ if TYPE_CHECKING:
7
+ from os import PathLike
8
+ from rattler.channel import Channel
9
+ from rattler.repo_data import PatchInstructions, RepoDataRecord
10
+
11
+ from rattler.rattler import PyRepoData
12
+
13
+
14
+ class RepoData:
15
+ def __init__(self, path: Union[str, PathLike[str]]) -> None:
16
+ if not isinstance(path, (str, Path)):
17
+ raise TypeError(
18
+ f"RepoData constructor received unsupported type {type(path).__name__!r} for the `path` parameter"
19
+ )
20
+
21
+ self._repo_data = PyRepoData.from_path(path)
22
+
23
+ def apply_patches(self, instructions: PatchInstructions) -> None:
24
+ """
25
+ Apply a patch to a repodata file.
26
+ Note that we currently do not handle revoked instructions.
27
+ """
28
+ self._repo_data.apply_patches(instructions._patch_instructions)
29
+
30
+ def into_repo_data(self, channel: Channel) -> List[RepoDataRecord]:
31
+ """
32
+ Builds a `List[RepoDataRecord]` from the packages in a
33
+ `RepoData` given the source of the data.
34
+
35
+ Examples
36
+ --------
37
+ ```python
38
+ >>> from rattler import Channel
39
+ >>> repo_data = RepoData("../test-data/test-server/repo/noarch/repodata.json")
40
+ >>> repo_data.into_repo_data(Channel("test"))
41
+ [...]
42
+ >>>
43
+ ```
44
+ """
45
+ from rattler.repo_data import RepoDataRecord
46
+
47
+ return [
48
+ RepoDataRecord._from_py_record(record)
49
+ for record in PyRepoData.repo_data_to_records(self._repo_data, channel._channel)
50
+ ]
51
+
52
+ @classmethod
53
+ def _from_py_repo_data(cls, py_repo_data: PyRepoData) -> RepoData:
54
+ """
55
+ Construct Rattler RepoData from FFI PyRepoData object.
56
+ """
57
+ repo_data = cls.__new__(cls)
58
+ repo_data._repo_data = py_repo_data
59
+ return repo_data
60
+
61
+ def __repr__(self) -> str:
62
+ """
63
+ Returns a representation of the RepoData.
64
+
65
+ Examples
66
+ --------
67
+ ```python
68
+ >>> repo_data = RepoData("../test-data/test-server/repo/noarch/repodata.json")
69
+ >>> repo_data
70
+ RepoData()
71
+ >>>
72
+ ```
73
+ """
74
+ return f"{type(self).__name__}()"
@@ -0,0 +1,85 @@
1
+ """Protocol definition for custom repodata sources."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, List, Protocol, runtime_checkable
6
+
7
+ if TYPE_CHECKING:
8
+ from rattler.platform.platform import Platform
9
+ from rattler.package.package_name import PackageName
10
+ from rattler.repo_data.record import RepoDataRecord
11
+
12
+
13
+ @runtime_checkable
14
+ class RepoDataSource(Protocol):
15
+ """Protocol for custom repodata sources.
16
+
17
+ Implement this protocol to provide repodata records from custom sources
18
+ (databases, APIs, in-memory caches, etc.) that can be used alongside
19
+ traditional conda channels.
20
+
21
+ Note
22
+ ----
23
+ Unlike channels, custom sources are **not cached** by the gateway. The gateway's
24
+ internal caching mechanisms only apply to channel data. If caching is needed for
25
+ your custom source, you must implement it yourself within your source implementation.
26
+
27
+ **Performance:** Custom sources are slower than channels because data must be
28
+ marshalled between Python and Rust for each request. For performance-critical
29
+ applications with large amounts of repodata, consider using channels when possible.
30
+
31
+ Example
32
+ -------
33
+ ```python
34
+ from rattler import Platform, PackageName, RepoDataRecord
35
+
36
+ class MyCustomSource:
37
+ async def fetch_package_records(
38
+ self, platform: Platform, name: PackageName
39
+ ) -> List[RepoDataRecord]:
40
+ # Fetch records from your custom source
41
+ return [...]
42
+
43
+ def package_names(self, platform: Platform) -> List[str]:
44
+ # Return all available package names for the platform
45
+ return ["numpy", "pandas", ...]
46
+
47
+ # Usage with Gateway
48
+ gateway = Gateway()
49
+ records = await gateway.query(
50
+ sources=[channel, MyCustomSource()], # Mix channels and custom sources
51
+ platforms=["linux-64"],
52
+ specs=["numpy"],
53
+ )
54
+ ```
55
+ """
56
+
57
+ async def fetch_package_records(self, platform: Platform, name: PackageName) -> List[RepoDataRecord]:
58
+ """Fetch records for a specific package name and platform.
59
+
60
+ This method is called by the gateway when it needs repodata records
61
+ for a particular package. The platform parameter indicates which
62
+ subdirectory the gateway is querying for.
63
+
64
+ Args:
65
+ platform: The platform to fetch records for (e.g., linux-64, noarch)
66
+ name: The package name to fetch records for
67
+
68
+ Returns:
69
+ List of RepoDataRecord objects for the package
70
+ """
71
+ ...
72
+
73
+ def package_names(self, platform: Platform) -> List[str]:
74
+ """Return all available package names for the given platform.
75
+
76
+ This is used by the gateway to know which packages are available
77
+ in this source for a given platform/subdirectory.
78
+
79
+ Args:
80
+ platform: The platform to list packages for
81
+
82
+ Returns:
83
+ List of package name strings
84
+ """
85
+ ...
@@ -0,0 +1,356 @@
1
+ from __future__ import annotations
2
+ import os
3
+ from pathlib import Path
4
+ from typing import List, Optional, Type, Literal, Iterable
5
+ from types import TracebackType
6
+
7
+ from rattler.match_spec.match_spec import MatchSpec
8
+ from rattler.channel.channel import Channel
9
+ from rattler.package.package_name import PackageName
10
+ from enum import Enum
11
+
12
+ from rattler.rattler import PySparseRepoData, PyPackageFormatSelection
13
+ from rattler.repo_data.record import RepoDataRecord
14
+
15
+
16
+ class PackageFormatSelection(Enum):
17
+ """
18
+ Enum that describes what to do if both a `.tar.bz2` and a `.conda` package is available.
19
+ """
20
+
21
+ ONLY_TAR_BZ2 = PyPackageFormatSelection.OnlyTarBz2
22
+ """
23
+ Only use the `.tar.bz2` packages, ignore all `.conda` packages.
24
+ """
25
+
26
+ ONLY_CONDA = PyPackageFormatSelection.OnlyConda
27
+ """
28
+ Only use the `.conda` packages, ignore all `.tar.bz2` packages.
29
+ """
30
+
31
+ PREFER_CONDA = PyPackageFormatSelection.PreferConda
32
+ """
33
+ Only use the `.conda` packages if there are both a `.tar.bz2` and a `.conda` package available.
34
+ """
35
+
36
+ PREFER_CONDA_WITH_WHL = PyPackageFormatSelection.PreferCondaWithWhl
37
+ """
38
+ Only use the `.conda` packages if there are both a `.tar.bz2` and a `.conda` package available.
39
+ Also adds `.whl` files if available.
40
+ """
41
+
42
+ BOTH = PyPackageFormatSelection.Both
43
+ """
44
+ Use both the `.tar.bz2` and the `.conda` packages.
45
+ """
46
+
47
+
48
+ class SparseRepoData:
49
+ """
50
+ A class to enable loading records from a `repodata.json` file on demand.
51
+ Since most of the time you don't need all the records from the `repodata.json`
52
+ this can help provide some significant speedups.
53
+ """
54
+
55
+ def __init__(
56
+ self,
57
+ channel: Channel,
58
+ subdir: str,
59
+ path: os.PathLike[str] | str,
60
+ ) -> None:
61
+ if not isinstance(channel, Channel):
62
+ raise TypeError(
63
+ "SparseRepoData constructor received unsupported type "
64
+ f" {type(channel).__name__!r} for the `channel` parameter"
65
+ )
66
+ if not isinstance(subdir, str):
67
+ raise TypeError(
68
+ "SparseRepoData constructor received unsupported type "
69
+ f" {type(subdir).__name__!r} for the `subdir` parameter"
70
+ )
71
+ if not isinstance(path, (str, Path)):
72
+ raise TypeError(
73
+ "SparseRepoData constructor received unsupported type "
74
+ f" {type(path).__name__!r} for the `path` parameter"
75
+ )
76
+ self._sparse = PySparseRepoData(channel._channel, subdir, str(path))
77
+
78
+ def close(self) -> None:
79
+ """
80
+ Closes any mapped resources associated with this `SparseRepoData`
81
+ instance. It is good practice to call this method when you are done
82
+ with it. This is especially important if you want to modify or delete
83
+ the file from which this instance was created.
84
+
85
+ This method will release all resources associated with this instance,
86
+ including those that are currently being used on another thread. This
87
+ method will block until all resources are released.
88
+
89
+ This method has no effect if the file is already closed. Once the
90
+ instance is closed, any operation on the instance will raise a
91
+ `ValueError`.
92
+
93
+ As a convenience, it is allowed to call this method more than once;
94
+ only the first call, however, will have an effect.
95
+
96
+ Examples
97
+ --------
98
+ ```python
99
+ >>> from rattler import Channel, ChannelConfig
100
+ >>> channel = Channel("dummy", ChannelConfig())
101
+ >>> path = "../test-data/channels/dummy/linux-64/repodata.json"
102
+ >>> sparse_data = SparseRepoData(channel, "linux-64", path)
103
+ >>> sparse_data.close()
104
+ >>> sparse_data.package_names() # doctest: +IGNORE_EXCEPTION_DETAIL
105
+ Traceback (most recent call last):
106
+ ValueError: I/O operation on closed file.
107
+ >>>
108
+ ```
109
+ """
110
+ self._sparse.close()
111
+
112
+ def package_names(
113
+ self, package_format_selection: PackageFormatSelection = PackageFormatSelection.PREFER_CONDA
114
+ ) -> List[str]:
115
+ """
116
+ Returns a list over all package names in this repodata file.
117
+ This works by iterating over all elements in the `packages` and
118
+ `conda_packages` fields of the repodata and returning the unique
119
+ package names.
120
+
121
+ Examples
122
+ --------
123
+ ```python
124
+ >>> from rattler import Channel, ChannelConfig
125
+ >>> channel = Channel("dummy", ChannelConfig())
126
+ >>> path = "../test-data/channels/dummy/linux-64/repodata.json"
127
+ >>> sparse_data = SparseRepoData(channel, "linux-64", path)
128
+ >>> package_names = sparse_data.package_names()
129
+ >>> package_names
130
+ [...]
131
+ >>> isinstance(package_names[0], str)
132
+ True
133
+ >>>
134
+ ```
135
+ """
136
+ return self._sparse.package_names(package_format_selection.value)
137
+
138
+ def record_count(
139
+ self, package_format_selection: PackageFormatSelection = PackageFormatSelection.PREFER_CONDA
140
+ ) -> int:
141
+ """
142
+ Returns the total number of packages in this repodata file.
143
+ :return:
144
+ """
145
+ return self._sparse.record_count(package_format_selection.value)
146
+
147
+ def load_records(
148
+ self,
149
+ package_name: str | PackageName,
150
+ package_format_selection: PackageFormatSelection = PackageFormatSelection.PREFER_CONDA,
151
+ ) -> List[RepoDataRecord]:
152
+ """
153
+ Returns all the records for the specified package name.
154
+
155
+ Examples
156
+ --------
157
+ ```python
158
+ >>> from rattler import Channel, ChannelConfig, RepoDataRecord, PackageName
159
+ >>> channel = Channel("dummy", ChannelConfig())
160
+ >>> path = "../test-data/channels/dummy/linux-64/repodata.json"
161
+ >>> sparse_data = SparseRepoData(channel, "linux-64", path)
162
+ >>> package_name = PackageName(sparse_data.package_names()[0])
163
+ >>> records = sparse_data.load_records(package_name)
164
+ >>> records
165
+ [...]
166
+ >>> isinstance(records[0], RepoDataRecord)
167
+ True
168
+ >>>
169
+ ```
170
+ """
171
+ if not isinstance(package_name, PackageName):
172
+ package_name = PackageName(package_name)
173
+ return [
174
+ RepoDataRecord._from_py_record(record)
175
+ for record in self._sparse.load_records(package_name._name, package_format_selection.value)
176
+ ]
177
+
178
+ def load_all_records(
179
+ self, package_format_selection: PackageFormatSelection = PackageFormatSelection.PREFER_CONDA
180
+ ) -> List[RepoDataRecord]:
181
+ """
182
+ Returns all the records for the specified package name.
183
+
184
+ Examples
185
+ --------
186
+ ```python
187
+ >>> from rattler import Channel, ChannelConfig, RepoDataRecord, PackageName
188
+ >>> channel = Channel("dummy", ChannelConfig())
189
+ >>> path = "../test-data/channels/dummy/linux-64/repodata.json"
190
+ >>> sparse_data = SparseRepoData(channel, "linux-64", path)
191
+ >>> records = sparse_data.load_all_records()
192
+ >>> records
193
+ [...]
194
+ >>> isinstance(records[0], RepoDataRecord)
195
+ True
196
+ >>>
197
+ ```
198
+ """
199
+ # maybe change package_name to Union[str, PackageName]
200
+ return [
201
+ RepoDataRecord._from_py_record(record)
202
+ for record in self._sparse.load_all_records(package_format_selection.value)
203
+ ]
204
+
205
+ def load_matching_records(
206
+ self,
207
+ specs: Iterable[MatchSpec],
208
+ package_format_selection: PackageFormatSelection = PackageFormatSelection.PREFER_CONDA,
209
+ ) -> List[RepoDataRecord]:
210
+ """
211
+ Returns all the records that match any of the specified MatchSpecs.
212
+
213
+ Examples
214
+ --------
215
+ ```python
216
+ >>> from rattler import Channel, ChannelConfig, RepoDataRecord, PackageName
217
+ >>> channel = Channel("dummy", ChannelConfig())
218
+ >>> path = "../test-data/channels/dummy/linux-64/repodata.json"
219
+ >>> sparse_data = SparseRepoData(channel, "linux-64", path)
220
+ >>> [record.file_name for record in sparse_data.load_matching_records([MatchSpec("* 12.5")])]
221
+ ['cuda-version-12.5-hd4f0392_3.conda']
222
+ >>>
223
+ ```
224
+ """
225
+ return [
226
+ RepoDataRecord._from_py_record(record)
227
+ for record in self._sparse.load_matching_records(
228
+ [spec._match_spec for spec in specs], package_format_selection.value
229
+ )
230
+ ]
231
+
232
+ @property
233
+ def subdir(self) -> str:
234
+ """
235
+ Returns the subdirectory from which this repodata was loaded.
236
+
237
+ Examples
238
+ --------
239
+ ```python
240
+ >>> from rattler import Channel, ChannelConfig
241
+ >>> channel = Channel("dummy", ChannelConfig())
242
+ >>> path = "../test-data/channels/dummy/linux-64/repodata.json"
243
+ >>> sparse_data = SparseRepoData(channel, "linux-64", path)
244
+ >>> sparse_data.subdir
245
+ 'linux-64'
246
+ >>>
247
+ ```
248
+ """
249
+ return self._sparse.subdir
250
+
251
+ @staticmethod
252
+ def load_records_recursive(
253
+ repo_data: List[SparseRepoData],
254
+ package_names: List[PackageName],
255
+ package_format_selection: PackageFormatSelection = PackageFormatSelection.PREFER_CONDA,
256
+ ) -> List[List[RepoDataRecord]]:
257
+ """
258
+ Given a set of [`SparseRepoData`]s load all the records
259
+ for the packages with the specified names and all the packages
260
+ these records depend on. This will parse the records for the
261
+ specified packages as well as all the packages these records
262
+ depend on.
263
+
264
+ Examples
265
+ --------
266
+ ```python
267
+ >>> from rattler import Channel, ChannelConfig, PackageName
268
+ >>> channel = Channel("dummy")
269
+ >>> subdir = "test-data/dummy/linux-64"
270
+ >>> path = "../test-data/channels/dummy/linux-64/repodata.json"
271
+ >>> sparse_data = SparseRepoData(channel, "linux-64", path)
272
+ >>> package_name = PackageName("python")
273
+ >>> SparseRepoData.load_records_recursive([sparse_data], [package_name])
274
+ [...]
275
+ >>>
276
+ ```
277
+ """
278
+ return [
279
+ [RepoDataRecord._from_py_record(record) for record in list_of_records]
280
+ for list_of_records in PySparseRepoData.load_records_recursive(
281
+ [r._sparse for r in repo_data], [p._name for p in package_names], package_format_selection.value
282
+ )
283
+ ]
284
+
285
+ @classmethod
286
+ def _from_py_sparse_repo_data(cls, py_sparse_repo_data: PySparseRepoData) -> SparseRepoData:
287
+ """
288
+ Construct Rattler SparseRepoData from FFI PySparseRepoData object.
289
+ """
290
+ sparse_repo_data = cls.__new__(cls)
291
+ sparse_repo_data._sparse = py_sparse_repo_data
292
+ return sparse_repo_data
293
+
294
+ def __repr__(self) -> str:
295
+ """
296
+ Returns a representation of the SparseRepoData.
297
+
298
+ Examples
299
+ --------
300
+ ```python
301
+ >>> from rattler import Channel, ChannelConfig
302
+ >>> channel = Channel("dummy", ChannelConfig())
303
+ >>> path = "../test-data/channels/dummy/linux-64/repodata.json"
304
+ >>> sparse_data = SparseRepoData(channel, "linux-64", path)
305
+ >>> sparse_data
306
+ SparseRepoData(subdir="linux-64")
307
+ >>>
308
+ ```
309
+ """
310
+ return f'SparseRepoData(subdir="{self.subdir}")'
311
+
312
+ def __enter__(self) -> SparseRepoData:
313
+ """
314
+ Returns the `SparseRepoData` instance itself. This is used to
315
+ enable the use of the `with` statement to automatically close
316
+ the instance when done.
317
+
318
+ Examples
319
+ --------
320
+ ```python
321
+ >>> from rattler import Channel, ChannelConfig
322
+ >>> channel = Channel("dummy", ChannelConfig())
323
+ >>> path = "../test-data/channels/dummy/linux-64/repodata.json"
324
+ >>> with SparseRepoData(channel, "linux-64", path) as sparse_data:
325
+ ... print(sparse_data)
326
+ ...
327
+ SparseRepoData(subdir="linux-64")
328
+ >>>
329
+ ```
330
+ """
331
+ return self
332
+
333
+ def __exit__(
334
+ self,
335
+ exctype: Optional[Type[BaseException]],
336
+ excinst: Optional[BaseException],
337
+ exctb: Optional[TracebackType],
338
+ ) -> Literal[False]:
339
+ """
340
+ Closes the `SparseRepoData` instance when exiting the `with` statement.
341
+
342
+ Examples
343
+ --------
344
+ ```python
345
+ >>> from rattler import Channel, ChannelConfig
346
+ >>> channel = Channel("dummy", ChannelConfig())
347
+ >>> path = "../test-data/channels/dummy/linux-64/repodata.json"
348
+ >>> with SparseRepoData(channel, "linux-64", path) as sparse_data:
349
+ ... print(sparse_data)
350
+ ...
351
+ SparseRepoData(subdir="linux-64")
352
+ >>>
353
+ ```
354
+ """
355
+ self.close()
356
+ return False