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.
- py_rattler-0.22.0.dist-info/METADATA +208 -0
- py_rattler-0.22.0.dist-info/RECORD +68 -0
- py_rattler-0.22.0.dist-info/WHEEL +4 -0
- rattler/__init__.py +114 -0
- rattler/channel/__init__.py +5 -0
- rattler/channel/channel.py +94 -0
- rattler/channel/channel_config.py +43 -0
- rattler/channel/channel_priority.py +14 -0
- rattler/exceptions.py +120 -0
- rattler/explicit_environment/__init__.py +3 -0
- rattler/explicit_environment/environment.py +69 -0
- rattler/index/__init__.py +3 -0
- rattler/index/index.py +112 -0
- rattler/install/__init__.py +3 -0
- rattler/install/installer.py +96 -0
- rattler/lock/__init__.py +23 -0
- rattler/lock/channel.py +52 -0
- rattler/lock/environment.py +213 -0
- rattler/lock/hash.py +33 -0
- rattler/lock/lock_file.py +118 -0
- rattler/lock/package.py +302 -0
- rattler/match_spec/__init__.py +4 -0
- rattler/match_spec/match_spec.py +294 -0
- rattler/match_spec/nameless_match_spec.py +185 -0
- rattler/networking/__init__.py +21 -0
- rattler/networking/client.py +74 -0
- rattler/networking/fetch_repo_data.py +103 -0
- rattler/networking/middleware.py +234 -0
- rattler/package/__init__.py +26 -0
- rattler/package/about_json.py +329 -0
- rattler/package/index_json.py +437 -0
- rattler/package/no_arch_type.py +142 -0
- rattler/package/package_name.py +204 -0
- rattler/package/package_name_matcher.py +81 -0
- rattler/package/paths_json.py +696 -0
- rattler/package/run_exports_json.py +268 -0
- rattler/package_streaming/__init__.py +26 -0
- rattler/platform/__init__.py +4 -0
- rattler/platform/arch.py +59 -0
- rattler/platform/platform.py +217 -0
- rattler/prefix/__init__.py +4 -0
- rattler/prefix/prefix_paths.py +442 -0
- rattler/prefix/prefix_record.py +234 -0
- rattler/pty/__init__.py +25 -0
- rattler/pty/pty_process.py +391 -0
- rattler/pty/pty_session.py +241 -0
- rattler/py.typed +0 -0
- rattler/rattler.abi3.so +0 -0
- rattler/repo_data/__init__.py +19 -0
- rattler/repo_data/gateway.py +337 -0
- rattler/repo_data/package_record.py +938 -0
- rattler/repo_data/patch_instructions.py +22 -0
- rattler/repo_data/record.py +164 -0
- rattler/repo_data/repo_data.py +74 -0
- rattler/repo_data/source.py +85 -0
- rattler/repo_data/sparse.py +356 -0
- rattler/shell/__init__.py +3 -0
- rattler/shell/shell.py +134 -0
- rattler/solver/__init__.py +3 -0
- rattler/solver/solver.py +220 -0
- rattler/utils/rattler_version.py +19 -0
- rattler/version/__init__.py +5 -0
- rattler/version/version.py +591 -0
- rattler/version/version_spec.py +184 -0
- rattler/version/with_source.py +80 -0
- rattler/virtual_package/__init__.py +4 -0
- rattler/virtual_package/generic.py +136 -0
- rattler/virtual_package/virtual_package.py +201 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Optional
|
|
4
|
+
|
|
5
|
+
from rattler.channel.channel import Channel
|
|
6
|
+
from rattler.package.package_name_matcher import PackageNameMatcher
|
|
7
|
+
from rattler.rattler import PyMatchSpec
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from rattler.match_spec import NamelessMatchSpec
|
|
11
|
+
from rattler.repo_data import PackageRecord
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class MatchSpec:
|
|
15
|
+
"""
|
|
16
|
+
A `MatchSpec` is a query language for conda packages.
|
|
17
|
+
It can be composed of any of the attributes of `PackageRecord`.
|
|
18
|
+
|
|
19
|
+
`MatchSpec` can be composed of keyword arguments, where keys are
|
|
20
|
+
any of the attributes of `PackageRecord`. Values for keyword arguments
|
|
21
|
+
are exact values the attributes should match against. Many fields can
|
|
22
|
+
be matched against non-exact values by including wildcard `*` and `>`/`<`
|
|
23
|
+
ranges where supported. Any non-specified field is the equivalent of a
|
|
24
|
+
full wildcard match.
|
|
25
|
+
|
|
26
|
+
MatchSpecs can also be composed using a single positional argument, with optional
|
|
27
|
+
keyword arguments. Keyword arguments also override any conflicting information
|
|
28
|
+
provided in the positional argument. Conda has historically had several string
|
|
29
|
+
representations for equivalent MatchSpecs.
|
|
30
|
+
|
|
31
|
+
A series of rules are now followed for creating the canonical string
|
|
32
|
+
representation of a MatchSpec instance. The canonical string representation can
|
|
33
|
+
generically be represented by:
|
|
34
|
+
|
|
35
|
+
`(channel(/subdir):(namespace):)name(version(build))[key1=value1,key2=value2]`
|
|
36
|
+
|
|
37
|
+
where `()` indicate optional fields.
|
|
38
|
+
|
|
39
|
+
The rules for constructing a canonical string representation are:
|
|
40
|
+
|
|
41
|
+
1. `name` (i.e. "package name") is required, but its value can be '*'. Its
|
|
42
|
+
position is always outside the key-value brackets.
|
|
43
|
+
It can also be a glob pattern or a regex if `exact_names_only` is `False`.
|
|
44
|
+
2. If `version` is an exact version, it goes outside the key-value brackets and
|
|
45
|
+
is prepended by `==`. If `version` is a "fuzzy" value (e.g. `1.11.*`), it goes
|
|
46
|
+
outside the key-value brackets with the `.*` left off and is prepended by `=`.
|
|
47
|
+
Otherwise `version` is included inside key-value brackets.
|
|
48
|
+
3. If `version` is an exact version, and `build` is an exact value, `build` goes
|
|
49
|
+
outside key-value brackets prepended by a `=`. Otherwise, `build` goes inside
|
|
50
|
+
key-value brackets. `build_string` is an alias for `build`.
|
|
51
|
+
4. The `namespace` position is being held for a future feature. It is currently
|
|
52
|
+
ignored.
|
|
53
|
+
5. If `channel` is included and is an exact value, a `::` separator is used between
|
|
54
|
+
`channel` and `name`. `channel` can either be a canonical channel name or a
|
|
55
|
+
channel url. In the canonical string representation, the canonical channel name
|
|
56
|
+
will always be used.
|
|
57
|
+
6. If `channel` is an exact value and `subdir` is an exact value, `subdir` is
|
|
58
|
+
appended to `channel` with a `/` separator. Otherwise, `subdir` is included in
|
|
59
|
+
the key-value brackets.
|
|
60
|
+
7. Key-value brackets can be delimited by comma, space, or comma+space. Value can
|
|
61
|
+
optionally be wrapped in single or double quotes, but must be wrapped if `value`
|
|
62
|
+
contains a comma, space, or equal sign. The canonical format uses comma delimiters
|
|
63
|
+
and single quotes.
|
|
64
|
+
8. When constructing a `MatchSpec` instance from a string, any key-value pair given
|
|
65
|
+
inside the key-value brackets overrides any matching parameter given outside the
|
|
66
|
+
brackets.
|
|
67
|
+
|
|
68
|
+
When `MatchSpec` attribute values are simple strings, the are interpreted using the
|
|
69
|
+
following conventions:
|
|
70
|
+
- If the string begins with `^` and ends with `$`, it is converted to a regex.
|
|
71
|
+
- If the string contains an asterisk (`*`), it is transformed from a glob to a
|
|
72
|
+
regex.
|
|
73
|
+
- Otherwise, an exact match to the string is sought.
|
|
74
|
+
|
|
75
|
+
To fully-specify a package with a full, exact spec, the following fields must be
|
|
76
|
+
given as exact values:
|
|
77
|
+
- channel
|
|
78
|
+
- subdir
|
|
79
|
+
- name
|
|
80
|
+
- version
|
|
81
|
+
- build
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
def __init__(
|
|
85
|
+
self,
|
|
86
|
+
spec: str,
|
|
87
|
+
strict: bool = False,
|
|
88
|
+
exact_names_only: bool = True,
|
|
89
|
+
experimental_extras: bool = False,
|
|
90
|
+
experimental_conditionals: bool = False,
|
|
91
|
+
) -> None:
|
|
92
|
+
"""
|
|
93
|
+
Create a new version spec.
|
|
94
|
+
|
|
95
|
+
When `strict` is `True`, some ambiguous version specs are rejected.
|
|
96
|
+
|
|
97
|
+
When `experimental_extras` is `True`, extras syntax is enabled (e.g., `pkg[extras=[foo,bar]]`).
|
|
98
|
+
|
|
99
|
+
When `experimental_conditionals` is `True`, conditionals syntax is enabled (e.g., `pkg; if python >=3.6`).
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
>>> MatchSpec("pip >=24.0")
|
|
103
|
+
MatchSpec("pip >=24.0")
|
|
104
|
+
>>> MatchSpec("pip 24")
|
|
105
|
+
MatchSpec("pip ==24")
|
|
106
|
+
>>> MatchSpec("python[license=MIT]")
|
|
107
|
+
MatchSpec("python[license="MIT"]")
|
|
108
|
+
>>> MatchSpec("foo*", strict=True, exact_names_only=False)
|
|
109
|
+
MatchSpec("foo*")
|
|
110
|
+
>>> MatchSpec("^foo.*$", strict=True, exact_names_only=True) # doctest: +IGNORE_EXCEPTION_DETAIL
|
|
111
|
+
Traceback (most recent call last):
|
|
112
|
+
InvalidMatchSpecException: "^foo.*$" looks like a regex but only exact package names are allowed, package names can only contain 0-9, a-z, A-Z, -, _, or .
|
|
113
|
+
>>>
|
|
114
|
+
```
|
|
115
|
+
"""
|
|
116
|
+
if isinstance(spec, str):
|
|
117
|
+
self._match_spec = PyMatchSpec(
|
|
118
|
+
spec, strict, exact_names_only, experimental_extras, experimental_conditionals
|
|
119
|
+
)
|
|
120
|
+
else:
|
|
121
|
+
raise TypeError(
|
|
122
|
+
f"MatchSpec constructor received unsupported type {type(spec).__name__!r} for the 'spec' parameter"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def name(self) -> Optional[PackageNameMatcher]:
|
|
127
|
+
"""
|
|
128
|
+
The name of the package.
|
|
129
|
+
"""
|
|
130
|
+
return PackageNameMatcher._from_py_package_name_matcher(self._match_spec.name)
|
|
131
|
+
|
|
132
|
+
@property
|
|
133
|
+
def version(self) -> Optional[str]:
|
|
134
|
+
"""
|
|
135
|
+
The version spec of the package (e.g. `1.2.3`, `>=1.2.3`, `1.2.*`)
|
|
136
|
+
"""
|
|
137
|
+
return self._match_spec.version
|
|
138
|
+
|
|
139
|
+
@property
|
|
140
|
+
def build(self) -> Optional[str]:
|
|
141
|
+
"""
|
|
142
|
+
The build string of the package (e.g. `py37_0`, `py37h6de7cb9_0`, `py*`)
|
|
143
|
+
"""
|
|
144
|
+
return self._match_spec.build
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def build_number(self) -> Optional[str]:
|
|
148
|
+
"""
|
|
149
|
+
The build number of the package.
|
|
150
|
+
"""
|
|
151
|
+
return self._match_spec.build_number
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def file_name(self) -> Optional[str]:
|
|
155
|
+
"""
|
|
156
|
+
Match the specific filename of the package.
|
|
157
|
+
"""
|
|
158
|
+
return self._match_spec.file_name
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def channel(self) -> Optional[Channel]:
|
|
162
|
+
"""
|
|
163
|
+
The channel of the package.
|
|
164
|
+
"""
|
|
165
|
+
channel = self._match_spec.channel
|
|
166
|
+
return channel and Channel._from_py_channel(channel)
|
|
167
|
+
|
|
168
|
+
@property
|
|
169
|
+
def subdir(self) -> Optional[str]:
|
|
170
|
+
"""
|
|
171
|
+
The subdir of the channel.
|
|
172
|
+
"""
|
|
173
|
+
return self._match_spec.subdir
|
|
174
|
+
|
|
175
|
+
@property
|
|
176
|
+
def namespace(self) -> Optional[str]:
|
|
177
|
+
"""
|
|
178
|
+
The namespace of the package.
|
|
179
|
+
"""
|
|
180
|
+
return self._match_spec.namespace
|
|
181
|
+
|
|
182
|
+
@property
|
|
183
|
+
def extras(self) -> Optional[list[str]]:
|
|
184
|
+
"""
|
|
185
|
+
The extras (optional dependencies) of the package.
|
|
186
|
+
"""
|
|
187
|
+
return self._match_spec.extras
|
|
188
|
+
|
|
189
|
+
@property
|
|
190
|
+
def condition(self) -> Optional[str]:
|
|
191
|
+
"""
|
|
192
|
+
The condition under which this match spec applies.
|
|
193
|
+
"""
|
|
194
|
+
return self._match_spec.condition
|
|
195
|
+
|
|
196
|
+
@property
|
|
197
|
+
def md5(self) -> Optional[bytes]:
|
|
198
|
+
"""
|
|
199
|
+
The md5 hash of the package.
|
|
200
|
+
"""
|
|
201
|
+
return self._match_spec.md5
|
|
202
|
+
|
|
203
|
+
@property
|
|
204
|
+
def sha256(self) -> Optional[bytes]:
|
|
205
|
+
"""
|
|
206
|
+
The sha256 hash of the package.
|
|
207
|
+
"""
|
|
208
|
+
return self._match_spec.sha256
|
|
209
|
+
|
|
210
|
+
@classmethod
|
|
211
|
+
def _from_py_match_spec(cls, py_match_spec: PyMatchSpec) -> MatchSpec:
|
|
212
|
+
"""
|
|
213
|
+
Construct py-rattler MatchSpec from PyMatchSpec FFI object.
|
|
214
|
+
"""
|
|
215
|
+
match_spec = cls.__new__(cls)
|
|
216
|
+
match_spec._match_spec = py_match_spec
|
|
217
|
+
|
|
218
|
+
return match_spec
|
|
219
|
+
|
|
220
|
+
def matches(self, record: PackageRecord) -> bool:
|
|
221
|
+
"""Match a MatchSpec against a PackageRecord."""
|
|
222
|
+
return self._match_spec.matches(record._record)
|
|
223
|
+
|
|
224
|
+
@classmethod
|
|
225
|
+
def from_nameless(cls, spec: NamelessMatchSpec, name: str) -> MatchSpec:
|
|
226
|
+
"""
|
|
227
|
+
Constructs a MatchSpec from a NamelessMatchSpec
|
|
228
|
+
and a name.
|
|
229
|
+
|
|
230
|
+
Examples
|
|
231
|
+
--------
|
|
232
|
+
```python
|
|
233
|
+
>>> from rattler import NamelessMatchSpec
|
|
234
|
+
>>> spec = NamelessMatchSpec('3.4')
|
|
235
|
+
>>> MatchSpec.from_nameless(spec, "foo")
|
|
236
|
+
MatchSpec("foo ==3.4")
|
|
237
|
+
>>> MatchSpec.from_nameless(spec, "$foo") # doctest: +IGNORE_EXCEPTION_DETAIL
|
|
238
|
+
Traceback (most recent call last):
|
|
239
|
+
exceptions.PackageNameMatcherParseException
|
|
240
|
+
>>>
|
|
241
|
+
```
|
|
242
|
+
"""
|
|
243
|
+
return cls._from_py_match_spec(PyMatchSpec.from_nameless(spec._nameless_match_spec, name))
|
|
244
|
+
|
|
245
|
+
@classmethod
|
|
246
|
+
def from_url(cls, url: str) -> MatchSpec:
|
|
247
|
+
"""
|
|
248
|
+
Constructs a MatchSpec from a URL.
|
|
249
|
+
|
|
250
|
+
Examples
|
|
251
|
+
--------
|
|
252
|
+
```python
|
|
253
|
+
>>> MatchSpec.from_url('https://repo.anaconda.com/pkgs/main/linux-64/python-3.9.0-h3.tar.bz2')
|
|
254
|
+
MatchSpec("python[url="https://repo.anaconda.com/pkgs/main/linux-64/python-3.9.0-h3.tar.bz2"]")
|
|
255
|
+
>>> MatchSpec.from_url('https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81')
|
|
256
|
+
MatchSpec("_libgcc_mutex[md5="d7c89558ba9fa0495403155b64376d81", url="https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2"]")
|
|
257
|
+
>>> MatchSpec.from_url('https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#sha256:adfa71f158cbd872a36394c56c3568e6034aa55c623634b37a4836bd036e6b91')
|
|
258
|
+
MatchSpec("_libgcc_mutex[sha256="adfa71f158cbd872a36394c56c3568e6034aa55c623634b37a4836bd036e6b91", url="https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2"]")
|
|
259
|
+
>>>
|
|
260
|
+
```
|
|
261
|
+
"""
|
|
262
|
+
return cls._from_py_match_spec(PyMatchSpec.from_url(url))
|
|
263
|
+
|
|
264
|
+
def __str__(self) -> str:
|
|
265
|
+
"""
|
|
266
|
+
Returns a string representation of the MatchSpec.
|
|
267
|
+
|
|
268
|
+
Examples
|
|
269
|
+
--------
|
|
270
|
+
```python
|
|
271
|
+
>>> from rattler import NamelessMatchSpec
|
|
272
|
+
>>> spec = NamelessMatchSpec('3.4')
|
|
273
|
+
>>> str(MatchSpec.from_nameless(spec, "foo"))
|
|
274
|
+
'foo ==3.4'
|
|
275
|
+
>>>
|
|
276
|
+
```
|
|
277
|
+
"""
|
|
278
|
+
return self._match_spec.as_str()
|
|
279
|
+
|
|
280
|
+
def __repr__(self) -> str:
|
|
281
|
+
"""
|
|
282
|
+
Returns a representation of the MatchSpec.
|
|
283
|
+
|
|
284
|
+
Examples
|
|
285
|
+
--------
|
|
286
|
+
```python
|
|
287
|
+
>>> from rattler import NamelessMatchSpec
|
|
288
|
+
>>> spec = NamelessMatchSpec('3.4')
|
|
289
|
+
>>> MatchSpec.from_nameless(spec, "foo")
|
|
290
|
+
MatchSpec("foo ==3.4")
|
|
291
|
+
>>>
|
|
292
|
+
```
|
|
293
|
+
"""
|
|
294
|
+
return f'MatchSpec("{self._match_spec.as_str()}")'
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import TYPE_CHECKING, Optional
|
|
3
|
+
from rattler.channel.channel import Channel
|
|
4
|
+
|
|
5
|
+
from rattler.rattler import PyNamelessMatchSpec
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from rattler.match_spec import MatchSpec
|
|
9
|
+
from rattler.repo_data import PackageRecord
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class NamelessMatchSpec:
|
|
13
|
+
"""
|
|
14
|
+
Similar to a `MatchSpec` but does not include the package name.
|
|
15
|
+
This is useful in places where the package name is already known
|
|
16
|
+
(e.g. `foo = "3.4.1 *cuda"`).
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
spec: str,
|
|
22
|
+
strict: bool = False,
|
|
23
|
+
experimental_extras: bool = False,
|
|
24
|
+
experimental_conditionals: bool = False,
|
|
25
|
+
) -> None:
|
|
26
|
+
"""
|
|
27
|
+
Create a new version spec.
|
|
28
|
+
|
|
29
|
+
When `strict` is `True`, some ambiguous version specs are rejected.
|
|
30
|
+
|
|
31
|
+
When `experimental_extras` is `True`, extras syntax is enabled (e.g., `[extras=[foo,bar]]`).
|
|
32
|
+
|
|
33
|
+
When `experimental_conditionals` is `True`, conditionals syntax is enabled (e.g., `>=1.0; if python >=3.6`).
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
>>> NamelessMatchSpec(">=24.0")
|
|
37
|
+
NamelessMatchSpec(">=24.0")
|
|
38
|
+
>>> NamelessMatchSpec("24")
|
|
39
|
+
NamelessMatchSpec("==24")
|
|
40
|
+
>>>
|
|
41
|
+
```
|
|
42
|
+
"""
|
|
43
|
+
if isinstance(spec, str):
|
|
44
|
+
self._nameless_match_spec = PyNamelessMatchSpec(
|
|
45
|
+
spec, strict, experimental_extras, experimental_conditionals
|
|
46
|
+
)
|
|
47
|
+
else:
|
|
48
|
+
raise TypeError(
|
|
49
|
+
"NamelessMatchSpec constructor received unsupported type"
|
|
50
|
+
f" {type(spec).__name__!r} for the 'spec' parameter"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def version(self) -> Optional[str]:
|
|
55
|
+
"""
|
|
56
|
+
The version spec of the package (e.g. `1.2.3`, `>=1.2.3`, `1.2.*`)
|
|
57
|
+
"""
|
|
58
|
+
return self._nameless_match_spec.version
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def build(self) -> Optional[str]:
|
|
62
|
+
"""
|
|
63
|
+
The build string of the package (e.g. `py37_0`, `py37h6de7cb9_0`, `py*`)
|
|
64
|
+
"""
|
|
65
|
+
return self._nameless_match_spec.build
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def build_number(self) -> Optional[str]:
|
|
69
|
+
"""
|
|
70
|
+
The build number of the package.
|
|
71
|
+
"""
|
|
72
|
+
return self._nameless_match_spec.build_number
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def file_name(self) -> Optional[str]:
|
|
76
|
+
"""
|
|
77
|
+
Match the specific filename of the package.
|
|
78
|
+
"""
|
|
79
|
+
return self._nameless_match_spec.file_name
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def channel(self) -> Optional[Channel]:
|
|
83
|
+
"""
|
|
84
|
+
The channel of the package.
|
|
85
|
+
"""
|
|
86
|
+
channel = self._nameless_match_spec.channel
|
|
87
|
+
return channel and Channel._from_py_channel(channel)
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def subdir(self) -> Optional[str]:
|
|
91
|
+
"""
|
|
92
|
+
The subdir of the channel.
|
|
93
|
+
"""
|
|
94
|
+
return self._nameless_match_spec.subdir
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def namespace(self) -> Optional[str]:
|
|
98
|
+
"""
|
|
99
|
+
The namespace of the package.
|
|
100
|
+
"""
|
|
101
|
+
return self._nameless_match_spec.namespace
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def md5(self) -> Optional[bytes]:
|
|
105
|
+
"""
|
|
106
|
+
The md5 hash of the package.
|
|
107
|
+
"""
|
|
108
|
+
return self._nameless_match_spec.md5
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def sha256(self) -> Optional[bytes]:
|
|
112
|
+
"""
|
|
113
|
+
The sha256 hash of the package.
|
|
114
|
+
"""
|
|
115
|
+
return self._nameless_match_spec.sha256
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def extras(self) -> Optional[list[str]]:
|
|
119
|
+
"""The extras (optional dependencies) of the package."""
|
|
120
|
+
return self._nameless_match_spec.extras
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def condition(self) -> Optional[str]:
|
|
124
|
+
"""The condition under which this match spec applies."""
|
|
125
|
+
return self._nameless_match_spec.condition
|
|
126
|
+
|
|
127
|
+
def matches(self, package_record: PackageRecord) -> bool:
|
|
128
|
+
"""
|
|
129
|
+
Match a MatchSpec against a PackageRecord
|
|
130
|
+
"""
|
|
131
|
+
return self._nameless_match_spec.matches(package_record._record)
|
|
132
|
+
|
|
133
|
+
@classmethod
|
|
134
|
+
def _from_py_nameless_match_spec(cls, py_nameless_match_spec: PyNamelessMatchSpec) -> NamelessMatchSpec:
|
|
135
|
+
"""
|
|
136
|
+
Construct py-rattler NamelessMatchSpec from PyNamelessMatchSpec FFI object.
|
|
137
|
+
"""
|
|
138
|
+
nameless_match_spec = cls.__new__(cls)
|
|
139
|
+
nameless_match_spec._nameless_match_spec = py_nameless_match_spec
|
|
140
|
+
|
|
141
|
+
return nameless_match_spec
|
|
142
|
+
|
|
143
|
+
@classmethod
|
|
144
|
+
def from_match_spec(cls, spec: MatchSpec) -> NamelessMatchSpec:
|
|
145
|
+
"""
|
|
146
|
+
Constructs a NamelessMatchSpec from a MatchSpec.
|
|
147
|
+
|
|
148
|
+
Examples
|
|
149
|
+
--------
|
|
150
|
+
```python
|
|
151
|
+
>>> from rattler import MatchSpec
|
|
152
|
+
>>> NamelessMatchSpec.from_match_spec(MatchSpec("foo ==3.4"))
|
|
153
|
+
NamelessMatchSpec("==3.4")
|
|
154
|
+
>>>
|
|
155
|
+
```
|
|
156
|
+
"""
|
|
157
|
+
return cls._from_py_nameless_match_spec(PyNamelessMatchSpec.from_match_spec(spec._match_spec))
|
|
158
|
+
|
|
159
|
+
def __str__(self) -> str:
|
|
160
|
+
"""
|
|
161
|
+
Returns a string representation of the NamelessMatchSpec.
|
|
162
|
+
|
|
163
|
+
Examples
|
|
164
|
+
--------
|
|
165
|
+
```python
|
|
166
|
+
>>> str(NamelessMatchSpec("3.4"))
|
|
167
|
+
'==3.4'
|
|
168
|
+
>>>
|
|
169
|
+
```
|
|
170
|
+
"""
|
|
171
|
+
return self._nameless_match_spec.as_str()
|
|
172
|
+
|
|
173
|
+
def __repr__(self) -> str:
|
|
174
|
+
"""
|
|
175
|
+
Returns a representation of the NamelessMatchSpec.
|
|
176
|
+
|
|
177
|
+
Examples
|
|
178
|
+
--------
|
|
179
|
+
```python
|
|
180
|
+
>>> NamelessMatchSpec("3.4")
|
|
181
|
+
NamelessMatchSpec("==3.4")
|
|
182
|
+
>>>
|
|
183
|
+
```
|
|
184
|
+
"""
|
|
185
|
+
return f'NamelessMatchSpec("{self._nameless_match_spec.as_str()}")'
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from rattler.networking.client import Client
|
|
2
|
+
from rattler.networking.fetch_repo_data import fetch_repo_data, CacheAction, FetchRepoDataOptions
|
|
3
|
+
from rattler.networking.middleware import (
|
|
4
|
+
AddHeadersMiddleware,
|
|
5
|
+
AuthenticationMiddleware,
|
|
6
|
+
GCSMiddleware,
|
|
7
|
+
MirrorMiddleware,
|
|
8
|
+
S3Middleware,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"fetch_repo_data",
|
|
13
|
+
"FetchRepoDataOptions",
|
|
14
|
+
"CacheAction",
|
|
15
|
+
"Client",
|
|
16
|
+
"AddHeadersMiddleware",
|
|
17
|
+
"MirrorMiddleware",
|
|
18
|
+
"AuthenticationMiddleware",
|
|
19
|
+
"S3Middleware",
|
|
20
|
+
"GCSMiddleware",
|
|
21
|
+
]
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from rattler.networking.middleware import (
|
|
4
|
+
AddHeadersMiddleware,
|
|
5
|
+
AuthenticationMiddleware,
|
|
6
|
+
GCSMiddleware,
|
|
7
|
+
MirrorMiddleware,
|
|
8
|
+
OciMiddleware,
|
|
9
|
+
S3Middleware,
|
|
10
|
+
)
|
|
11
|
+
from rattler.rattler import PyClientWithMiddleware
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Client:
|
|
15
|
+
"""
|
|
16
|
+
A client that can be used to make requests.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
middlewares: (
|
|
22
|
+
list[
|
|
23
|
+
AddHeadersMiddleware
|
|
24
|
+
| AuthenticationMiddleware
|
|
25
|
+
| MirrorMiddleware
|
|
26
|
+
| OciMiddleware
|
|
27
|
+
| GCSMiddleware
|
|
28
|
+
| S3Middleware
|
|
29
|
+
]
|
|
30
|
+
| None
|
|
31
|
+
) = None,
|
|
32
|
+
headers: dict[str, str] | None = None,
|
|
33
|
+
) -> None:
|
|
34
|
+
self._client = PyClientWithMiddleware(
|
|
35
|
+
[middleware._middleware for middleware in middlewares] if middlewares else None, headers
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def _from_ffi_object(cls, client: PyClientWithMiddleware) -> Client:
|
|
40
|
+
"""
|
|
41
|
+
Construct py-rattler Client from PyClientWithMiddleware FFI object.
|
|
42
|
+
"""
|
|
43
|
+
client = cls.__new__(cls)
|
|
44
|
+
client._client = client
|
|
45
|
+
return client
|
|
46
|
+
|
|
47
|
+
def __repr__(self) -> str:
|
|
48
|
+
"""
|
|
49
|
+
Returns a representation of the Client
|
|
50
|
+
|
|
51
|
+
Examples
|
|
52
|
+
--------
|
|
53
|
+
```python
|
|
54
|
+
>>> Client()
|
|
55
|
+
Client()
|
|
56
|
+
>>>
|
|
57
|
+
```
|
|
58
|
+
"""
|
|
59
|
+
return f"{type(self).__name__}()"
|
|
60
|
+
|
|
61
|
+
@staticmethod
|
|
62
|
+
def authenticated_client() -> Client:
|
|
63
|
+
"""
|
|
64
|
+
Returns an authenticated client.
|
|
65
|
+
|
|
66
|
+
Examples
|
|
67
|
+
--------
|
|
68
|
+
```python
|
|
69
|
+
>>> Client.authenticated_client()
|
|
70
|
+
Client()
|
|
71
|
+
>>>
|
|
72
|
+
```
|
|
73
|
+
"""
|
|
74
|
+
return Client([AuthenticationMiddleware()])
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Callable, List, Literal, Optional, Union, TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from rattler.networking.client import Client
|
|
6
|
+
from rattler.rattler import py_fetch_repo_data, PyFetchRepoDataOptions
|
|
7
|
+
from rattler.repo_data.sparse import SparseRepoData
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
import os
|
|
11
|
+
from rattler.channel import Channel
|
|
12
|
+
from rattler.platform import Platform
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
CacheAction = Literal["cache-or-fetch", "use-cache-only", "force-cache-only", "no-cache"]
|
|
16
|
+
Variant = Literal["after-patches", "from-packages", "current"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class FetchRepoDataOptions:
|
|
21
|
+
cache_action: CacheAction = "cache-or-fetch"
|
|
22
|
+
"""How to interact with the cache.
|
|
23
|
+
|
|
24
|
+
* `'cache-or-fetch'` (default): Use the cache if its up to date or fetch from the URL if there is no valid cached value.
|
|
25
|
+
* `'use-cache-only'`: Only use the cache, but error out if the cache is not up to date
|
|
26
|
+
* `'force-cache-only'`: Only use the cache, ignore whether or not it is up to date.
|
|
27
|
+
* `'no-cache'`: Do not use the cache even if there is an up to date entry
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
variant: Variant = "after-patches"
|
|
31
|
+
"""Which type of repodata to download
|
|
32
|
+
|
|
33
|
+
* `'after-patches'` (default): Fetch the `repodata.json` file. This `repodata.json` has repodata patches applied.
|
|
34
|
+
* `'from-packages'` Fetch the `repodata_from_packages.json` file
|
|
35
|
+
* `'current'`: Fetch `current_repodata.json` file. This file contains only the latest version of each package.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
jlap_enabled: bool = True
|
|
39
|
+
"""Whether the JLAP compression is enabled or not."""
|
|
40
|
+
|
|
41
|
+
zstd_enabled: bool = True
|
|
42
|
+
"""Whether the ZSTD compression is enabled or not."""
|
|
43
|
+
|
|
44
|
+
bz2_enabled: bool = True
|
|
45
|
+
"""Whether the BZ2 compression is enabled or not."""
|
|
46
|
+
|
|
47
|
+
def _into_py(self) -> PyFetchRepoDataOptions:
|
|
48
|
+
"""
|
|
49
|
+
Converts this object into a type that can be used by the Rust code.
|
|
50
|
+
|
|
51
|
+
Examples
|
|
52
|
+
--------
|
|
53
|
+
```python
|
|
54
|
+
>>> FetchRepoDataOptions()._into_py() # doctest: +ELLIPSIS
|
|
55
|
+
<builtins.PyFetchRepoDataOptions object at 0x...>
|
|
56
|
+
>>>
|
|
57
|
+
```
|
|
58
|
+
"""
|
|
59
|
+
return PyFetchRepoDataOptions(
|
|
60
|
+
cache_action=self.cache_action,
|
|
61
|
+
variant=self.variant,
|
|
62
|
+
jlap_enabled=self.jlap_enabled,
|
|
63
|
+
zstd_enabled=self.zstd_enabled,
|
|
64
|
+
bz2_enabled=self.bz2_enabled,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
async def fetch_repo_data(
|
|
69
|
+
*,
|
|
70
|
+
channels: List[Channel],
|
|
71
|
+
platforms: List[Platform],
|
|
72
|
+
cache_path: Union[str, os.PathLike[str]],
|
|
73
|
+
callback: Optional[Callable[[int, int], None]],
|
|
74
|
+
client: Optional[Client] = None,
|
|
75
|
+
fetch_options: Optional[FetchRepoDataOptions] = None,
|
|
76
|
+
) -> List[SparseRepoData]:
|
|
77
|
+
"""
|
|
78
|
+
Returns a list of RepoData for given channels and platform.
|
|
79
|
+
|
|
80
|
+
Arguments:
|
|
81
|
+
channels: A list of `Channel`s to fetch repo data.
|
|
82
|
+
platforms: A list of `Platform`s for which the repo data
|
|
83
|
+
should be fetched.
|
|
84
|
+
cache_path: A `os.PathLike[str]` where the repo data should
|
|
85
|
+
be downloaded.
|
|
86
|
+
callback: A `Callable[[int, int], None]` to report the download
|
|
87
|
+
progress of repo data.
|
|
88
|
+
client: A `Client` to use for fetching the repo data.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
A list of `SparseRepoData` for requested channels and platforms.
|
|
92
|
+
"""
|
|
93
|
+
fetch_options = fetch_options or FetchRepoDataOptions()
|
|
94
|
+
repo_data_list = await py_fetch_repo_data(
|
|
95
|
+
[channel._channel for channel in channels],
|
|
96
|
+
[platform._inner for platform in platforms],
|
|
97
|
+
cache_path,
|
|
98
|
+
callback,
|
|
99
|
+
client._client if client else None,
|
|
100
|
+
fetch_options._into_py(),
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
return [SparseRepoData._from_py_sparse_repo_data(repo_data) for repo_data in repo_data_list]
|