1 | 7 | # -*- coding: utf-8 -*- |
|
2 | from __future__ import absolute_import, division, print_function, unicode_literals |
||
3 | 7 | ||
4 | 7 | from .common.constants import NULL |
|
5 | 7 | from .core.package_cache_data import PackageCacheData as _PackageCacheData |
|
6 | 7 | from .core.prefix_data import PrefixData as _PrefixData |
|
7 | 7 | from .core.solve import (DepsModifier as _DepsModifier, Solver as _Solver, |
|
8 | 7 | UpdateModifier as _UpdateModifier) |
|
9 | from .core.subdir_data import SubdirData as _SubdirData |
||
10 | 7 | from .models.channel import Channel |
|
11 | |||
12 | DepsModifier = _DepsModifier |
||
13 | """Flags to enable alternate handling of dependencies.""" |
||
14 | |||
15 | UpdateModifier = _UpdateModifier |
||
16 | """Flags to enable alternate handling for updates of existing packages in the environment.""" |
||
17 | |||
18 | |||
19 | 7 | class Solver(object): |
|
20 | """ |
||
21 | 7 | **Beta** While in beta, expect both major and minor changes across minor releases. |
|
22 | 7 | ||
23 | 7 | A high-level API to conda's solving logic. Three public methods are provided to access a |
|
24 | 7 | solution in various forms. |
|
25 | 7 | ||
26 | 7 | * :meth:`solve_final_state` |
|
27 | 7 | * :meth:`solve_for_diff` |
|
28 | 7 | * :meth:`solve_for_transaction` |
|
29 | 7 | ||
30 | 7 | """ |
|
31 | 7 | ||
32 | 7 | def __init__(self, prefix, channels, subdirs=(), specs_to_add=(), specs_to_remove=()): |
|
33 | 7 | """ |
|
34 | 7 | **Beta** |
|
35 | |||
36 | 7 | Args: |
|
37 | 7 | prefix (str): |
|
38 | The conda prefix / environment location for which the :class:`Solver` |
||
39 | is being instantiated. |
||
40 | channels (Sequence[:class:`Channel`]): |
||
41 | 7 | A prioritized list of channels to use for the solution. |
|
42 | 7 | subdirs (Sequence[str]): |
|
43 | 7 | A prioritized list of subdirs to use for the solution. |
|
44 | 7 | specs_to_add (Set[:class:`MatchSpec`]): |
|
45 | The set of package specs to add to the prefix. |
||
46 | specs_to_remove (Set[:class:`MatchSpec`]): |
||
47 | 7 | The set of package specs to remove from the prefix. |
|
48 | |||
49 | """ |
||
50 | self._internal = _Solver(prefix, channels, subdirs, specs_to_add, specs_to_remove) |
||
51 | |||
52 | def solve_final_state(self, update_modifier=NULL, deps_modifier=NULL, prune=NULL, |
||
53 | ignore_pinned=NULL, force_remove=NULL): |
||
54 | """ |
||
55 | **Beta** While in beta, expect both major and minor changes across minor releases. |
||
56 | |||
57 | Gives the final, solved state of the environment. |
||
58 | |||
59 | Args: |
||
60 | deps_modifier (DepsModifier): |
||
61 | An optional flag indicating special solver handling for dependencies. The |
||
62 | default solver behavior is to be as conservative as possible with dependency |
||
63 | updates (in the case the dependency already exists in the environment), while |
||
64 | still ensuring all dependencies are satisfied. Options include |
||
65 | * NO_DEPS |
||
66 | * ONLY_DEPS |
||
67 | * UPDATE_DEPS |
||
68 | * UPDATE_DEPS_ONLY_DEPS |
||
69 | * FREEZE_INSTALLED |
||
70 | prune (bool): |
||
71 | If ``True``, the solution will not contain packages that were |
||
72 | previously brought into the environment as dependencies but are no longer |
||
73 | required as dependencies and are not user-requested. |
||
74 | ignore_pinned (bool): |
||
75 | If ``True``, the solution will ignore pinned package configuration |
||
76 | for the prefix. |
||
77 | force_remove (bool): |
||
78 | Forces removal of a package without removing packages that depend on it. |
||
79 | |||
80 | Returns: |
||
81 | Tuple[PackageRef]: |
||
82 | In sorted dependency order from roots to leaves, the package references for |
||
83 | the solved state of the environment. |
||
84 | |||
85 | """ |
||
86 | return self._internal.solve_final_state(update_modifier, deps_modifier, prune, |
||
87 | ignore_pinned, force_remove) |
||
88 | |||
89 | def solve_for_diff(self, update_modifier=NULL, deps_modifier=NULL, prune=NULL, |
||
90 | ignore_pinned=NULL, force_remove=NULL, force_reinstall=False): |
||
91 | """ |
||
92 | **Beta** While in beta, expect both major and minor changes across minor releases. |
||
93 | |||
94 | Gives the package references to remove from an environment, followed by |
||
95 | the package references to add to an environment. |
||
96 | |||
97 | Args: |
||
98 | deps_modifier (DepsModifier): |
||
99 | See :meth:`solve_final_state`. |
||
100 | prune (bool): |
||
101 | See :meth:`solve_final_state`. |
||
102 | ignore_pinned (bool): |
||
103 | See :meth:`solve_final_state`. |
||
104 | force_remove (bool): |
||
105 | See :meth:`solve_final_state`. |
||
106 | force_reinstall (bool): |
||
107 | For requested specs_to_add that are already satisfied in the environment, |
||
108 | instructs the solver to remove the package and spec from the environment, |
||
109 | and then add it back--possibly with the exact package instance modified, |
||
110 | depending on the spec exactness. |
||
111 | |||
112 | Returns: |
||
113 | Tuple[PackageRef], Tuple[PackageRef]: |
||
114 | A two-tuple of PackageRef sequences. The first is the group of packages to |
||
115 | remove from the environment, in sorted dependency order from leaves to roots. |
||
116 | The second is the group of packages to add to the environment, in sorted |
||
117 | dependency order from roots to leaves. |
||
118 | |||
119 | """ |
||
120 | return self._internal.solve_for_diff(update_modifier, deps_modifier, prune, ignore_pinned, |
||
121 | force_remove, force_reinstall) |
||
122 | |||
123 | def solve_for_transaction(self, update_modifier=NULL, deps_modifier=NULL, prune=NULL, |
||
124 | ignore_pinned=NULL, force_remove=NULL, force_reinstall=False): |
||
125 | """ |
||
126 | **Beta** While in beta, expect both major and minor changes across minor releases. |
||
127 | |||
128 | Gives an UnlinkLinkTransaction instance that can be used to execute the solution |
||
129 | on an environment. |
||
130 | |||
131 | Args: |
||
132 | deps_modifier (DepsModifier): |
||
133 | See :meth:`solve_final_state`. |
||
134 | prune (bool): |
||
135 | See :meth:`solve_final_state`. |
||
136 | ignore_pinned (bool): |
||
137 | See :meth:`solve_final_state`. |
||
138 | force_remove (bool): |
||
139 | See :meth:`solve_final_state`. |
||
140 | force_reinstall (bool): |
||
141 | See :meth:`solve_for_diff`. |
||
142 | |||
143 | Returns: |
||
144 | UnlinkLinkTransaction: |
||
145 | |||
146 | """ |
||
147 | return self._internal.solve_for_transaction(update_modifier, deps_modifier, prune, |
||
148 | View Code Duplication | ignore_pinned, force_remove, force_reinstall) |
|
0 ignored issues
–
show
Duplication
introduced
by
Loading history...
|
|||
149 | |||
150 | |||
151 | class SubdirData(object): |
||
152 | """ |
||
153 | **Beta** While in beta, expect both major and minor changes across minor releases. |
||
154 | |||
155 | High-level management and usage of repodata.json for subdirs. |
||
156 | """ |
||
157 | |||
158 | def __init__(self, channel): |
||
159 | """ |
||
160 | **Beta** While in beta, expect both major and minor changes across minor releases. |
||
161 | |||
162 | Args: |
||
163 | channel (str or Channel): |
||
164 | The target subdir for the instance. Must either be a url that includes a subdir |
||
165 | or a :obj:`Channel` that includes a subdir. e.g.: |
||
166 | * 'https://repo.anaconda.com/pkgs/main/linux-64' |
||
167 | * Channel('https://repo.anaconda.com/pkgs/main/linux-64') |
||
168 | * Channel('conda-forge/osx-64') |
||
169 | """ |
||
170 | channel = Channel(channel) |
||
171 | assert channel.subdir |
||
172 | self._internal = _SubdirData(channel) |
||
173 | |||
174 | def query(self, package_ref_or_match_spec): |
||
175 | """ |
||
176 | **Beta** While in beta, expect both major and minor changes across minor releases. |
||
177 | |||
178 | Run a query against this specific instance of repodata. |
||
179 | |||
180 | Args: |
||
181 | package_ref_or_match_spec (PackageRef or MatchSpec or str): |
||
182 | Either an exact :obj:`PackageRef` to match against, or a :obj:`MatchSpec` |
||
183 | query object. A :obj:`str` will be turned into a :obj:`MatchSpec` automatically. |
||
184 | |||
185 | Returns: |
||
186 | Tuple[PackageRecord] |
||
187 | |||
188 | """ |
||
189 | return tuple(self._internal.query(package_ref_or_match_spec)) |
||
190 | |||
191 | @staticmethod |
||
192 | def query_all(package_ref_or_match_spec, channels=None, subdirs=None): |
||
193 | """ |
||
194 | **Beta** While in beta, expect both major and minor changes across minor releases. |
||
195 | |||
196 | Run a query against all repodata instances in channel/subdir matrix. |
||
197 | |||
198 | Args: |
||
199 | package_ref_or_match_spec (PackageRef or MatchSpec or str): |
||
200 | Either an exact :obj:`PackageRef` to match against, or a :obj:`MatchSpec` |
||
201 | query object. A :obj:`str` will be turned into a :obj:`MatchSpec` automatically. |
||
202 | channels (Iterable[Channel or str] or None): |
||
203 | An iterable of urls for channels or :obj:`Channel` objects. If None, will fall |
||
204 | back to context.channels. |
||
205 | subdirs (Iterable[str] or None): |
||
206 | If None, will fall back to context.subdirs. |
||
207 | |||
208 | Returns: |
||
209 | Tuple[PackageRecord] |
||
210 | |||
211 | """ |
||
212 | return tuple(_SubdirData.query_all(package_ref_or_match_spec, channels, subdirs)) |
||
213 | |||
214 | def iter_records(self): |
||
215 | """ |
||
216 | **Beta** While in beta, expect both major and minor changes across minor releases. |
||
217 | |||
218 | Returns: |
||
219 | Iterable[PackageRecord]: A generator over all records contained in the repodata.json |
||
220 | instance. Warning: this is a generator that is exhausted on first use. |
||
221 | |||
222 | """ |
||
223 | return self._internal.iter_records() |
||
224 | |||
225 | def reload(self): |
||
226 | """ |
||
227 | **Beta** While in beta, expect both major and minor changes across minor releases. |
||
228 | |||
229 | Update the instance with new information. Backing information (i.e. repodata.json) |
||
230 | is lazily downloaded/loaded on first use by the other methods of this class. You |
||
231 | should only use this method if you are *sure* you have outdated data. |
||
232 | |||
233 | Returns: |
||
234 | SubdirData |
||
235 | |||
236 | """ |
||
237 | self._internal = self._internal.reload() |
||
238 | return self |
||
239 | |||
240 | |||
241 | class PackageCacheData(object): |
||
242 | """ |
||
243 | **Beta** While in beta, expect both major and minor changes across minor releases. |
||
244 | |||
245 | High-level management and usage of package caches. |
||
246 | """ |
||
247 | |||
248 | def __init__(self, pkgs_dir): |
||
249 | """ |
||
250 | **Beta** While in beta, expect both major and minor changes across minor releases. |
||
251 | |||
252 | Args: |
||
253 | pkgs_dir (str): |
||
254 | """ |
||
255 | self._internal = _PackageCacheData(pkgs_dir) |
||
256 | |||
257 | def get(self, package_ref, default=NULL): |
||
258 | """ |
||
259 | **Beta** While in beta, expect both major and minor changes across minor releases. |
||
260 | |||
261 | Args: |
||
262 | package_ref (PackageRef): |
||
263 | A :obj:`PackageRef` instance representing the key for the |
||
264 | :obj:`PackageCacheRecord` being sought. |
||
265 | default: The default value to return if the record does not exist. If not |
||
266 | specified and no record exists, :exc:`KeyError` is raised. |
||
267 | |||
268 | Returns: |
||
269 | PackageCacheRecord |
||
270 | |||
271 | """ |
||
272 | return self._internal.get(package_ref, default) |
||
273 | |||
274 | def query(self, package_ref_or_match_spec): |
||
275 | """ |
||
276 | **Beta** While in beta, expect both major and minor changes across minor releases. |
||
277 | |||
278 | Run a query against this specific package cache instance. |
||
279 | |||
280 | Args: |
||
281 | package_ref_or_match_spec (PackageRef or MatchSpec or str): |
||
282 | Either an exact :obj:`PackageRef` to match against, or a :obj:`MatchSpec` |
||
283 | query object. A :obj:`str` will be turned into a :obj:`MatchSpec` automatically. |
||
284 | |||
285 | Returns: |
||
286 | Tuple[PackageCacheRecord] |
||
287 | |||
288 | """ |
||
289 | return tuple(self._internal.query(package_ref_or_match_spec)) |
||
290 | |||
291 | @staticmethod |
||
292 | def query_all(package_ref_or_match_spec, pkgs_dirs=None): |
||
293 | """ |
||
294 | **Beta** While in beta, expect both major and minor changes across minor releases. |
||
295 | |||
296 | Run a query against all package caches. |
||
297 | |||
298 | Args: |
||
299 | package_ref_or_match_spec (PackageRef or MatchSpec or str): |
||
300 | Either an exact :obj:`PackageRef` to match against, or a :obj:`MatchSpec` |
||
301 | query object. A :obj:`str` will be turned into a :obj:`MatchSpec` automatically. |
||
302 | pkgs_dirs (Iterable[str] or None): |
||
303 | If None, will fall back to context.pkgs_dirs. |
||
304 | |||
305 | Returns: |
||
306 | Tuple[PackageCacheRecord] |
||
307 | |||
308 | """ |
||
309 | return tuple(_PackageCacheData.query_all(package_ref_or_match_spec, pkgs_dirs)) |
||
310 | |||
311 | def iter_records(self): |
||
312 | """ |
||
313 | **Beta** While in beta, expect both major and minor changes across minor releases. |
||
314 | |||
315 | Returns: |
||
316 | Iterable[PackageCacheRecord]: A generator over all records contained in the package |
||
317 | cache instance. Warning: this is a generator that is exhausted on first use. |
||
318 | |||
319 | """ |
||
320 | return self._internal.iter_records() |
||
321 | |||
322 | @property |
||
323 | def is_writable(self): |
||
324 | """ |
||
325 | **Beta** While in beta, expect both major and minor changes across minor releases. |
||
326 | |||
327 | Indicates if the package cache location is writable or read-only. |
||
328 | |||
329 | Returns: |
||
330 | bool |
||
331 | |||
332 | """ |
||
333 | return self._internal.is_writable |
||
334 | |||
335 | @staticmethod |
||
336 | def first_writable(pkgs_dirs=None): |
||
337 | """ |
||
338 | **Beta** While in beta, expect both major and minor changes across minor releases. |
||
339 | |||
340 | Get an instance object for the first writable package cache. |
||
341 | |||
342 | Args: |
||
343 | pkgs_dirs (Iterable[str]): |
||
344 | If None, will fall back to context.pkgs_dirs. |
||
345 | |||
346 | Returns: |
||
347 | PackageCacheData: |
||
348 | An instance for the first writable package cache. |
||
349 | |||
350 | """ |
||
351 | return PackageCacheData(_PackageCacheData.first_writable(pkgs_dirs).pkgs_dir) |
||
352 | |||
353 | def reload(self): |
||
354 | """ |
||
355 | **Beta** While in beta, expect both major and minor changes across minor releases. |
||
356 | |||
357 | Update the instance with new information. Backing information (i.e. contents of |
||
358 | the pkgs_dir) is lazily loaded on first use by the other methods of this class. You |
||
359 | should only use this method if you are *sure* you have outdated data. |
||
360 | |||
361 | Returns: |
||
362 | PackageCacheData |
||
363 | |||
364 | """ |
||
365 | self._internal = self._internal.reload() |
||
366 | View Code Duplication | return self |
|
0 ignored issues
–
show
|
|||
367 | |||
368 | |||
369 | class PrefixData(object): |
||
370 | """ |
||
371 | **Beta** While in beta, expect both major and minor changes across minor releases. |
||
372 | |||
373 | High-level management and usage of conda environment prefixes. |
||
374 | """ |
||
375 | |||
376 | def __init__(self, prefix_path): |
||
377 | """ |
||
378 | **Beta** While in beta, expect both major and minor changes across minor releases. |
||
379 | |||
380 | Args: |
||
381 | prefix_path (str): |
||
382 | """ |
||
383 | self._internal = _PrefixData(prefix_path) |
||
384 | |||
385 | def get(self, package_ref, default=NULL): |
||
386 | """ |
||
387 | **Beta** While in beta, expect both major and minor changes across minor releases. |
||
388 | |||
389 | Args: |
||
390 | package_ref (PackageRef): |
||
391 | A :obj:`PackageRef` instance representing the key for the |
||
392 | :obj:`PrefixRecord` being sought. |
||
393 | default: The default value to return if the record does not exist. If not |
||
394 | specified and no record exists, :exc:`KeyError` is raised. |
||
395 | |||
396 | Returns: |
||
397 | PrefixRecord |
||
398 | |||
399 | """ |
||
400 | return self._internal.get(package_ref.name, default) |
||
401 | |||
402 | def query(self, package_ref_or_match_spec): |
||
403 | """ |
||
404 | **Beta** While in beta, expect both major and minor changes across minor releases. |
||
405 | |||
406 | Run a query against this specific prefix instance. |
||
407 | |||
408 | Args: |
||
409 | package_ref_or_match_spec (PackageRef or MatchSpec or str): |
||
410 | Either an exact :obj:`PackageRef` to match against, or a :obj:`MatchSpec` |
||
411 | query object. A :obj:`str` will be turned into a :obj:`MatchSpec` automatically. |
||
412 | |||
413 | Returns: |
||
414 | Tuple[PrefixRecord] |
||
415 | |||
416 | """ |
||
417 | return tuple(self._internal.query(package_ref_or_match_spec)) |
||
418 | |||
419 | def iter_records(self): |
||
420 | """ |
||
421 | **Beta** While in beta, expect both major and minor changes across minor releases. |
||
422 | |||
423 | Returns: |
||
424 | Iterable[PrefixRecord]: A generator over all records contained in the prefix. |
||
425 | Warning: this is a generator that is exhausted on first use. |
||
426 | |||
427 | """ |
||
428 | return self._internal.iter_records() |
||
429 | |||
430 | @property |
||
431 | def is_writable(self): |
||
432 | """ |
||
433 | **Beta** While in beta, expect both major and minor changes across minor releases. |
||
434 | |||
435 | Indicates if the prefix is writable or read-only. |
||
436 | |||
437 | Returns: |
||
438 | bool or None: |
||
439 | True if the prefix is writable. False if read-only. None if the prefix |
||
440 | does not exist as a conda environment. |
||
441 | |||
442 | """ |
||
443 | return self._internal.is_writable |
||
444 | |||
445 | def reload(self): |
||
446 | """ |
||
447 | **Beta** While in beta, expect both major and minor changes across minor releases. |
||
448 | |||
449 | Update the instance with new information. Backing information (i.e. contents of |
||
450 | the conda-meta directory) is lazily loaded on first use by the other methods of this |
||
451 | class. You should only use this method if you are *sure* you have outdated data. |
||
452 | |||
453 | Returns: |
||
454 | PrefixData |
||
455 | |||
456 | """ |
||
457 | self._internal = self._internal.reload() |
||
458 | return self |
||
459 |