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,938 @@
1
+ from __future__ import annotations
2
+ import os
3
+ from typing import List, Optional, TYPE_CHECKING
4
+ import datetime
5
+
6
+ from rattler import VersionWithSource
7
+ from rattler.match_spec.match_spec import MatchSpec
8
+ from rattler.package.no_arch_type import NoArchType, NoArchLiteral
9
+ from rattler.package.package_name import PackageName
10
+ from rattler.platform.platform import Platform
11
+ from rattler.rattler import PyRecord, ParsePlatformError
12
+
13
+ if TYPE_CHECKING:
14
+ import networkx as nx
15
+
16
+
17
+ class PackageRecord:
18
+ """
19
+ A single record in the Conda repodata. A single
20
+ record refers to a single binary distribution
21
+ of a package on a Conda channel.
22
+ """
23
+
24
+ _record: PyRecord
25
+
26
+ def matches(self, spec: MatchSpec) -> bool:
27
+ """
28
+ Match a [`PackageRecord`] against a [`MatchSpec`].
29
+
30
+ Examples
31
+ --------
32
+ ```python
33
+ >>> from rattler import MatchSpec
34
+ >>> record = PackageRecord.from_index_json(
35
+ ... "../test-data/conda-meta/pysocks-1.7.1-pyh0701188_6.json"
36
+ ... )
37
+ >>> spec = MatchSpec("pysocks")
38
+ >>> record.matches(spec)
39
+ True
40
+ >>> spec = MatchSpec("pysocks>=1.7")
41
+ >>> record.matches(spec)
42
+ True
43
+ >>> spec = MatchSpec("pysocks<1.7")
44
+ >>> record.matches(spec)
45
+ False
46
+ >>>
47
+ ```
48
+ """
49
+ return spec.matches(self)
50
+
51
+ @staticmethod
52
+ def from_index_json(
53
+ path: os.PathLike[str],
54
+ size: Optional[int] = None,
55
+ sha256: Optional[str] = None,
56
+ md5: Optional[str] = None,
57
+ ) -> PackageRecord:
58
+ """
59
+ Builds a PackageRecord from an `index.json`.
60
+ These can be found in `info` directory inside an
61
+ extracted package archive.
62
+
63
+ Examples
64
+ --------
65
+ ```python
66
+ >>> record = PackageRecord.from_index_json(
67
+ ... "../test-data/conda-meta/pysocks-1.7.1-pyh0701188_6.json"
68
+ ... )
69
+ >>> assert isinstance(record, PackageRecord)
70
+ >>> record.name
71
+ PackageName("pysocks")
72
+ >>> record.version
73
+ VersionWithSource(version="1.7.1", source="1.7.1")
74
+ >>> record.build
75
+ 'pyh0701188_6'
76
+ >>>
77
+ ```
78
+ """
79
+ return PackageRecord._from_py_record(PyRecord.from_index_json(path, size, sha256, md5))
80
+
81
+ @staticmethod
82
+ def sort_topologically(records: List[PackageRecord]) -> List[PackageRecord]:
83
+ """
84
+ Sorts the records topologically.
85
+ This function is deterministic, meaning that it will return the same result
86
+ regardless of the order of records and of the depends vector inside the records.
87
+ Note that this function only works for packages with unique names.
88
+
89
+ Examples
90
+ --------
91
+ ```python
92
+ >>> from os import listdir
93
+ >>> from os.path import isfile, join
94
+ >>> from rattler import PrefixRecord
95
+ >>> records = [
96
+ ... PrefixRecord.from_path(join("../test-data/conda-meta/", f))
97
+ ... for f in listdir("../test-data/conda-meta")
98
+ ... if isfile(join("../test-data/conda-meta", f))
99
+ ... ]
100
+ >>> sorted = PackageRecord.sort_topologically(records)
101
+ >>> sorted[0].name
102
+ PackageName("python_abi")
103
+ >>> # Verify it's deterministic by sorting again
104
+ >>> sorted2 = PackageRecord.sort_topologically(records)
105
+ >>> [str(r) for r in sorted] == [str(r) for r in sorted2]
106
+ True
107
+ >>>
108
+ ```
109
+ """
110
+ return [PackageRecord._from_py_record(p) for p in PyRecord.sort_topologically(records)]
111
+
112
+ @staticmethod
113
+ def to_graph(records: List[PackageRecord]) -> nx.DiGraph: # type: ignore[type-arg]
114
+ """
115
+ Converts a list of PackageRecords to a DAG (`networkx.DiGraph`).
116
+ The nodes in the graph are the PackageRecords and the edges are the dependencies.
117
+
118
+ Note: Virtual packages (starting with `__`) are skipped.
119
+ """
120
+ try:
121
+ import networkx as nx
122
+ except ImportError:
123
+ raise ImportError("networkx is not installed") from None
124
+
125
+ names_to_records = {record.name: record for record in records}
126
+
127
+ graph = nx.DiGraph() # type: ignore[var-annotated]
128
+ for record in records:
129
+ graph.add_node(record)
130
+ for dep in record.depends:
131
+ name = dep.split(" ")[0]
132
+ if name.startswith("__"):
133
+ # this is a virtual package, so we just skip it
134
+ continue
135
+ graph.add_edge(record, names_to_records[PackageName(name)])
136
+
137
+ return graph
138
+
139
+ @staticmethod
140
+ def validate(records: List[PackageRecord]) -> None:
141
+ """
142
+ Validate that the given package records are valid w.r.t. 'depends' and 'constrains'.
143
+
144
+ This function will return nothing if all records form a valid environment, i.e., all dependencies
145
+ of each package are satisfied by the other packages in the list.
146
+ If there is a dependency that is not satisfied, this function will raise an exception.
147
+
148
+ Examples
149
+ --------
150
+ ```python
151
+ >>> from os import listdir
152
+ >>> from os.path import isfile, join
153
+ >>> from rattler import PrefixRecord
154
+ >>> from rattler.exceptions import ValidatePackageRecordsException
155
+ >>> records = [
156
+ ... PrefixRecord.from_path(join("../test-data/conda-meta/", f))
157
+ ... for f in sorted(listdir("../test-data/conda-meta"))
158
+ ... if isfile(join("../test-data/conda-meta", f))
159
+ ... ]
160
+ >>> try:
161
+ ... PackageRecord.validate(records)
162
+ ... except ValidatePackageRecordsException as e:
163
+ ... print(e)
164
+ package 'libsqlite=3.40.0=hcfcfb64_0' has dependency 'ucrt >=10.0.20348.0', which is not in the environment
165
+ >>>
166
+ ```
167
+ """
168
+ return PyRecord.validate(records)
169
+
170
+ @classmethod
171
+ def _from_py_record(cls, py_record: PyRecord) -> PackageRecord:
172
+ """
173
+ Construct Rattler PackageRecord from FFI PyRecord object.
174
+ """
175
+
176
+ # quick sanity check
177
+ assert py_record.is_package_record
178
+ record = cls.__new__(cls)
179
+ record._record = py_record
180
+ return record
181
+
182
+ def __init__(
183
+ self,
184
+ name: str | PackageName,
185
+ version: str | VersionWithSource,
186
+ build: str,
187
+ build_number: int,
188
+ subdir: str | Platform,
189
+ arch: Optional[str] = None,
190
+ platform: Optional[str] = None,
191
+ noarch: Optional[NoArchType | NoArchLiteral] = None,
192
+ depends: Optional[List[str]] = None,
193
+ constrains: Optional[List[str]] = None,
194
+ sha256: Optional[bytes] = None,
195
+ md5: Optional[bytes] = None,
196
+ size: Optional[int] = None,
197
+ features: Optional[List[str]] = None,
198
+ legacy_bz2_md5: Optional[bytes] = None,
199
+ legacy_bz2_size: Optional[int] = None,
200
+ license: Optional[str] = None,
201
+ license_family: Optional[str] = None,
202
+ python_site_packages_path: Optional[str] = None,
203
+ ) -> None:
204
+ if isinstance(subdir, str):
205
+ try:
206
+ subdir = Platform(subdir)
207
+ except ParsePlatformError:
208
+ # if the string is not a valid platform, we just keep it as a string
209
+ pass
210
+
211
+ if isinstance(subdir, Platform):
212
+ if arch is None:
213
+ subdir_arch = subdir.arch
214
+ arch = str(subdir_arch) if subdir_arch is not None else arch
215
+ platform = subdir.only_platform if platform is None else platform
216
+
217
+ # convert str to PackageName
218
+ if isinstance(name, str):
219
+ name = PackageName(name)
220
+
221
+ # convert str to VersionWithSource
222
+ if isinstance(version, str):
223
+ version = VersionWithSource(version)
224
+
225
+ if not isinstance(noarch, NoArchType):
226
+ noarch = NoArchType(noarch)
227
+
228
+ self._record = PyRecord.create(
229
+ name._name,
230
+ (version._version, version._source),
231
+ build,
232
+ build_number,
233
+ str(subdir),
234
+ str(arch) if arch is not None else None,
235
+ platform,
236
+ noarch._noarch,
237
+ python_site_packages_path,
238
+ )
239
+
240
+ if constrains is not None:
241
+ self._record.constrains = constrains
242
+ if depends is not None:
243
+ self._record.depends = depends
244
+ if sha256 is not None:
245
+ self._record.sha256 = sha256
246
+ if md5 is not None:
247
+ self._record.md5 = md5
248
+ if size is not None:
249
+ self._record.size = size
250
+ if features is not None:
251
+ self._record.features = features
252
+ if legacy_bz2_md5 is not None:
253
+ self._record.legacy_bz2_md5 = legacy_bz2_md5
254
+ if legacy_bz2_size is not None:
255
+ self._record.legacy_bz2_size = legacy_bz2_size
256
+ if license is not None:
257
+ self._record.license = license
258
+ if license_family is not None:
259
+ self._record.license_family = license_family
260
+
261
+ @property
262
+ def arch(self) -> Optional[str]:
263
+ """
264
+ Optionally the architecture the package supports.
265
+
266
+ Examples
267
+ --------
268
+ ```python
269
+ >>> from rattler import PrefixRecord
270
+ >>> record = PrefixRecord.from_path(
271
+ ... "../test-data/conda-meta/libsqlite-3.40.0-hcfcfb64_0.json"
272
+ ... )
273
+ >>> record.arch
274
+ 'x86_64'
275
+ >>> record.arch = "arm64"
276
+ >>> record.arch
277
+ 'arm64'
278
+ >>> record.arch = None
279
+ >>> record.arch is None
280
+ True
281
+ >>>
282
+ ```
283
+ """
284
+ return self._record.arch
285
+
286
+ @arch.setter
287
+ def arch(self, value: Optional[str]) -> None:
288
+ self._record.arch = value
289
+
290
+ @property
291
+ def build(self) -> str:
292
+ """
293
+ The build string of the package.
294
+
295
+ Examples
296
+ --------
297
+ ```python
298
+ >>> from rattler import PrefixRecord
299
+ >>> record = PrefixRecord.from_path(
300
+ ... "../test-data/conda-meta/libsqlite-3.40.0-hcfcfb64_0.json"
301
+ ... )
302
+ >>> record.build
303
+ 'hcfcfb64_0'
304
+ >>> record.build = "new_build_1"
305
+ >>> record.build
306
+ 'new_build_1'
307
+ >>>
308
+ ```
309
+ """
310
+ return self._record.build
311
+
312
+ @build.setter
313
+ def build(self, value: str) -> None:
314
+ self._record.build = value
315
+
316
+ @property
317
+ def build_number(self) -> int:
318
+ """
319
+ The build number of the package.
320
+
321
+ Examples
322
+ --------
323
+ ```python
324
+ >>> from rattler import PrefixRecord
325
+ >>> record = PrefixRecord.from_path(
326
+ ... "../test-data/conda-meta/libsqlite-3.40.0-hcfcfb64_0.json"
327
+ ... )
328
+ >>> record.build_number
329
+ 0
330
+ >>> record.build_number = 42
331
+ >>> record.build_number
332
+ 42
333
+ >>>
334
+ ```
335
+ """
336
+ return self._record.build_number
337
+
338
+ @build_number.setter
339
+ def build_number(self, value: int) -> None:
340
+ self._record.build_number = value
341
+
342
+ @property
343
+ def constrains(self) -> List[str]:
344
+ """
345
+ Additional constraints on packages.
346
+ Constrains are different from depends in that packages
347
+ specified in depends must be installed next to this package,
348
+ whereas packages specified in constrains are not required to
349
+ be installed, but if they are installed they must follow
350
+ these constraints.
351
+
352
+ Examples
353
+ --------
354
+ ```python
355
+ >>> from rattler import PrefixRecord
356
+ >>> record = PrefixRecord.from_path(
357
+ ... "../test-data/conda-meta/libsqlite-3.40.0-hcfcfb64_0.json"
358
+ ... )
359
+ >>> record.constrains
360
+ []
361
+ >>> record.constrains = ["python >=3.6"]
362
+ >>> record.constrains
363
+ ['python >=3.6']
364
+ >>>
365
+ ```
366
+ """
367
+ return self._record.constrains
368
+
369
+ @constrains.setter
370
+ def constrains(self, value: List[str]) -> None:
371
+ self._record.constrains = value
372
+
373
+ @property
374
+ def depends(self) -> List[str]:
375
+ """
376
+ Specification of packages this package depends on.
377
+
378
+ Examples
379
+ --------
380
+ ```python
381
+ >>> from rattler import PrefixRecord
382
+ >>> record = PrefixRecord.from_path(
383
+ ... "../test-data/conda-meta/libsqlite-3.40.0-hcfcfb64_0.json"
384
+ ... )
385
+ >>> record.depends
386
+ ['ucrt >=10.0.20348.0', 'vc >=14.2,<15', 'vs2015_runtime >=14.29.30139']
387
+ >>> record.depends = ["python >=3.6"]
388
+ >>> record.depends
389
+ ['python >=3.6']
390
+ >>>
391
+ ```
392
+ """
393
+ return self._record.depends
394
+
395
+ @depends.setter
396
+ def depends(self, value: List[str]) -> None:
397
+ self._record.depends = value
398
+
399
+ @property
400
+ def features(self) -> Optional[str]:
401
+ """
402
+ Features are a deprecated way to specify different feature
403
+ sets for the conda solver. This is not supported anymore and
404
+ should not be used. Instead, mutex packages should be used
405
+ to specify mutually exclusive features.
406
+
407
+ Examples
408
+ --------
409
+ ```python
410
+ >>> record = PackageRecord.from_index_json(
411
+ ... "../test-data/conda-meta/pysocks-1.7.1-pyh0701188_6.json"
412
+ ... )
413
+ >>> record.features is None
414
+ True
415
+ >>> record.features = "feature1"
416
+ >>> record.features
417
+ 'feature1'
418
+ >>> record.features = None
419
+ >>> record.features is None
420
+ True
421
+ >>>
422
+ ```
423
+ """
424
+ return self._record.features
425
+
426
+ @features.setter
427
+ def features(self, value: Optional[str]) -> None:
428
+ self._record.features = value
429
+
430
+ @property
431
+ def legacy_bz2_md5(self) -> Optional[bytes]:
432
+ """
433
+ A deprecated md5 hash.
434
+
435
+ Examples
436
+ --------
437
+ ```python
438
+ >>> record = PackageRecord.from_index_json(
439
+ ... "../test-data/conda-meta/pysocks-1.7.1-pyh0701188_6.json"
440
+ ... )
441
+ >>> record.legacy_bz2_md5 is None
442
+ True
443
+ >>> record.legacy_bz2_md5 = bytes.fromhex("2ddbbaf3a82b46ac7214681262e3d746")
444
+ >>> record.legacy_bz2_md5.hex()
445
+ '2ddbbaf3a82b46ac7214681262e3d746'
446
+ >>> record.legacy_bz2_md5 = None
447
+ >>> record.legacy_bz2_md5 is None
448
+ True
449
+ >>>
450
+ ```
451
+ """
452
+ return self._record.legacy_bz2_md5
453
+
454
+ @legacy_bz2_md5.setter
455
+ def legacy_bz2_md5(self, value: Optional[bytes]) -> None:
456
+ self._record.legacy_bz2_md5 = value
457
+
458
+ @property
459
+ def legacy_bz2_size(self) -> Optional[int]:
460
+ """
461
+ A deprecated package archive size.
462
+
463
+ Examples
464
+ --------
465
+ ```python
466
+ >>> record = PackageRecord.from_index_json(
467
+ ... "../test-data/conda-meta/pysocks-1.7.1-pyh0701188_6.json"
468
+ ... )
469
+ >>> record.legacy_bz2_size is None
470
+ True
471
+ >>> record.legacy_bz2_size = 42
472
+ >>> record.legacy_bz2_size
473
+ 42
474
+ >>> record.legacy_bz2_size = None
475
+ >>> record.legacy_bz2_size is None
476
+ True
477
+ >>>
478
+ ```
479
+ """
480
+ return self._record.legacy_bz2_size
481
+
482
+ @legacy_bz2_size.setter
483
+ def legacy_bz2_size(self, value: Optional[int]) -> None:
484
+ self._record.legacy_bz2_size = value
485
+
486
+ @property
487
+ def license(self) -> Optional[str]:
488
+ """
489
+ The specific license of the package.
490
+
491
+ Examples
492
+ --------
493
+ ```python
494
+ >>> from rattler import PrefixRecord
495
+ >>> record = PrefixRecord.from_path(
496
+ ... "../test-data/conda-meta/libsqlite-3.40.0-hcfcfb64_0.json"
497
+ ... )
498
+ >>> record.license
499
+ 'Unlicense'
500
+ >>> record.license = "MIT"
501
+ >>> record.license
502
+ 'MIT'
503
+ >>> record.license = None
504
+ >>> record.license is None
505
+ True
506
+ >>>
507
+ ```
508
+ """
509
+ return self._record.license
510
+
511
+ @license.setter
512
+ def license(self, value: Optional[str]) -> None:
513
+ self._record.license = value
514
+
515
+ @property
516
+ def license_family(self) -> Optional[str]:
517
+ """
518
+ The license family.
519
+
520
+ Examples
521
+ --------
522
+ ```python
523
+ >>> from rattler import PrefixRecord
524
+ >>> record = PrefixRecord.from_path(
525
+ ... "../test-data/conda-meta/pip-23.0-pyhd8ed1ab_0.json"
526
+ ... )
527
+ >>> record.license_family
528
+ 'MIT'
529
+ >>> record.license_family = "BSD"
530
+ >>> record.license_family
531
+ 'BSD'
532
+ >>> record.license_family = None
533
+ >>> record.license_family is None
534
+ True
535
+ >>>
536
+ ```
537
+
538
+ """
539
+ return self._record.license_family
540
+
541
+ @license_family.setter
542
+ def license_family(self, value: Optional[str]) -> None:
543
+ self._record.license_family = value
544
+
545
+ @property
546
+ def md5(self) -> Optional[bytes]:
547
+ """
548
+ Optionally a MD5 hash of the package archive.
549
+
550
+ Examples
551
+ --------
552
+ ```python
553
+ >>> from rattler import PrefixRecord
554
+ >>> record = PrefixRecord.from_path(
555
+ ... "../test-data/conda-meta/libsqlite-3.40.0-hcfcfb64_0.json"
556
+ ... )
557
+ >>> record.md5.hex()
558
+ '5e5a97795de72f8cc3baf3d9ea6327a2'
559
+ >>> record.md5 = bytes.fromhex("2ddbbaf3a82b46ac7214681262e3d746")
560
+ >>> record.md5.hex()
561
+ '2ddbbaf3a82b46ac7214681262e3d746'
562
+ >>> record.md5 = None
563
+ >>> record.md5 is None
564
+ True
565
+ >>>
566
+ ```
567
+ """
568
+ return self._record.md5
569
+
570
+ @md5.setter
571
+ def md5(self, value: Optional[bytes]) -> None:
572
+ self._record.md5 = value
573
+
574
+ @property
575
+ def name(self) -> PackageName:
576
+ """
577
+ The name of the package.
578
+
579
+ Examples
580
+ --------
581
+ ```python
582
+ >>> from rattler import PrefixRecord, PackageName
583
+ >>> record = PrefixRecord.from_path(
584
+ ... "../test-data/conda-meta/libsqlite-3.40.0-hcfcfb64_0.json"
585
+ ... )
586
+ >>> record.name
587
+ PackageName("libsqlite")
588
+ >>> record.name = PackageName("newname")
589
+ >>> record.name
590
+ PackageName("newname")
591
+ >>>
592
+ ```
593
+ """
594
+ return PackageName._from_py_package_name(self._record.name)
595
+
596
+ @name.setter
597
+ def name(self, value: PackageName) -> None:
598
+ self._record.name = value._name
599
+
600
+ @property
601
+ def noarch(self) -> NoArchType:
602
+ """
603
+ The noarch type of the package.
604
+
605
+ Examples
606
+ --------
607
+ ```python
608
+ >>> from rattler import PrefixRecord
609
+ >>> record = PrefixRecord.from_path(
610
+ ... "../test-data/conda-meta/libsqlite-3.40.0-hcfcfb64_0.json"
611
+ ... )
612
+ >>> record.noarch
613
+ NoArchType(None)
614
+ >>> record = PrefixRecord.from_path(
615
+ ... "../test-data/conda-meta/pip-23.0-pyhd8ed1ab_0.json"
616
+ ... )
617
+ >>> record.noarch
618
+ NoArchType("python")
619
+ >>> record.noarch = NoArchType("generic")
620
+ >>> record.noarch
621
+ NoArchType("generic")
622
+ >>> record.noarch = NoArchType(None)
623
+ >>> record.noarch.none
624
+ True
625
+ >>>
626
+ ```
627
+ """
628
+ return NoArchType._from_py_no_arch_type(self._record.noarch)
629
+
630
+ @noarch.setter
631
+ def noarch(self, value: NoArchType) -> None:
632
+ self._record.noarch = value._noarch
633
+
634
+ @property
635
+ def platform(self) -> Optional[str]:
636
+ """
637
+ Optionally the platform the package supports.
638
+
639
+ Examples
640
+ --------
641
+ ```python
642
+ >>> from rattler import PrefixRecord
643
+ >>> record = PrefixRecord.from_path(
644
+ ... "../test-data/conda-meta/libsqlite-3.40.0-hcfcfb64_0.json"
645
+ ... )
646
+ >>> record.platform
647
+ 'win32'
648
+ >>> record.platform = "linux"
649
+ >>> record.platform
650
+ 'linux'
651
+ >>> record.platform = None
652
+ >>> record.platform is None
653
+ True
654
+ >>>
655
+ ```
656
+ """
657
+ return self._record.platform
658
+
659
+ @platform.setter
660
+ def platform(self, value: Optional[str]) -> None:
661
+ self._record.platform = value
662
+
663
+ @property
664
+ def sha256(self) -> Optional[bytes]:
665
+ """
666
+ Optionally a SHA256 hash of the package archive.
667
+
668
+ Examples
669
+ --------
670
+ ```python
671
+ >>> from rattler import PrefixRecord
672
+ >>> record = PrefixRecord.from_path(
673
+ ... "../test-data/conda-meta/libsqlite-3.40.0-hcfcfb64_0.json"
674
+ ... )
675
+ >>> record.sha256.hex()
676
+ '4e50b3d90a351c9d47d239d3f90fce4870df2526e4f7fef35203ab3276a6dfc9'
677
+ >>> record.sha256 = bytes.fromhex("edd7dd24fc070fad8ca690a920d94b6312a376faa96b47c657f9ef5fe5a97dd1")
678
+ >>> record.sha256.hex()
679
+ 'edd7dd24fc070fad8ca690a920d94b6312a376faa96b47c657f9ef5fe5a97dd1'
680
+ >>> record.sha256 = None
681
+ >>> record.sha256 is None
682
+ True
683
+ >>>
684
+ ````
685
+ """
686
+ return self._record.sha256
687
+
688
+ @sha256.setter
689
+ def sha256(self, value: Optional[bytes]) -> None:
690
+ self._record.sha256 = value
691
+
692
+ @property
693
+ def size(self) -> Optional[int]:
694
+ """
695
+ Optionally the size of the package archive in bytes.
696
+
697
+ Examples
698
+ --------
699
+ ```python
700
+ >>> from rattler import PrefixRecord
701
+ >>> record = PrefixRecord.from_path(
702
+ ... "../test-data/conda-meta/libsqlite-3.40.0-hcfcfb64_0.json"
703
+ ... )
704
+ >>> record.size
705
+ 669941
706
+ >>> record.size = 42
707
+ >>> record.size
708
+ 42
709
+ >>> record.size = None
710
+ >>> record.size is None
711
+ True
712
+ >>>
713
+ ```
714
+ """
715
+ return self._record.size
716
+
717
+ @size.setter
718
+ def size(self, value: Optional[int]) -> None:
719
+ self._record.size = value
720
+
721
+ @property
722
+ def subdir(self) -> str:
723
+ """
724
+ The subdirectory where the package can be found.
725
+
726
+ Examples
727
+ --------
728
+ ```python
729
+ >>> from rattler import PrefixRecord
730
+ >>> record = PrefixRecord.from_path(
731
+ ... "../test-data/conda-meta/libsqlite-3.40.0-hcfcfb64_0.json"
732
+ ... )
733
+ >>> record.subdir
734
+ 'win-64'
735
+ >>> record.subdir = "linux-64"
736
+ >>> record.subdir
737
+ 'linux-64'
738
+ >>>
739
+ ```
740
+ """
741
+ return self._record.subdir
742
+
743
+ @subdir.setter
744
+ def subdir(self, value: str) -> None:
745
+ self._record.subdir = value
746
+
747
+ @property
748
+ def timestamp(self) -> Optional[datetime.datetime]:
749
+ """
750
+ The date this entry was created.
751
+
752
+ Examples
753
+ --------
754
+ ```python
755
+ >>> from rattler import PrefixRecord
756
+ >>> import datetime
757
+ >>> record = PrefixRecord.from_path(
758
+ ... "../test-data/conda-meta/libsqlite-3.40.0-hcfcfb64_0.json"
759
+ ... )
760
+ >>> record.timestamp
761
+ datetime.datetime(2022, 11, 17, 15, 7, 19, 781000, tzinfo=datetime.timezone.utc)
762
+ >>> new_time = datetime.datetime(2023, 1, 1, tzinfo=datetime.timezone.utc)
763
+ >>> record.timestamp = new_time
764
+ >>> record.timestamp
765
+ datetime.datetime(2023, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)
766
+ >>> record.timestamp = None
767
+ >>> record.timestamp is None
768
+ True
769
+ >>>
770
+ ```
771
+ """
772
+ if self._record.timestamp:
773
+ return datetime.datetime.fromtimestamp(self._record.timestamp / 1000.0, tz=datetime.timezone.utc)
774
+
775
+ return self._record.timestamp
776
+
777
+ @timestamp.setter
778
+ def timestamp(self, value: Optional[datetime.datetime]) -> None:
779
+ if value is not None:
780
+ self._record.timestamp = int(value.timestamp() * 1000)
781
+ else:
782
+ self._record.timestamp = None
783
+
784
+ @property
785
+ def track_features(self) -> List[str]:
786
+ """
787
+ Track features are nowadays only used to downweight
788
+ packages (ie. give them less priority).
789
+ To that effect, the number of track features is
790
+ counted (number of commas) and the package is downweighted
791
+ by the number of track_features.
792
+
793
+ Examples
794
+ --------
795
+ ```python
796
+ >>> from rattler import PrefixRecord
797
+ >>> record = PrefixRecord.from_path(
798
+ ... "../test-data/conda-meta/libsqlite-3.40.0-hcfcfb64_0.json"
799
+ ... )
800
+ >>> record.track_features
801
+ []
802
+ >>> record.track_features = ["feature1", "feature2"]
803
+ >>> record.track_features
804
+ ['feature1', 'feature2']
805
+ >>>
806
+ ```
807
+ """
808
+ return self._record.track_features
809
+
810
+ @track_features.setter
811
+ def track_features(self, value: List[str]) -> None:
812
+ self._record.track_features = value
813
+
814
+ @property
815
+ def version(self) -> VersionWithSource:
816
+ """
817
+ The version of the package.
818
+
819
+ Examples
820
+ --------
821
+ ```python
822
+ >>> from rattler import PrefixRecord, VersionWithSource
823
+ >>> record = PrefixRecord.from_path(
824
+ ... "../test-data/conda-meta/libsqlite-3.40.0-hcfcfb64_0.json"
825
+ ... )
826
+ >>> record.version
827
+ VersionWithSource(version="3.40.0", source="3.40.0")
828
+ >>> record.version = VersionWithSource("1.0.0")
829
+ >>> record.version
830
+ VersionWithSource(version="1.0.0", source="1.0.0")
831
+ >>>
832
+ ```
833
+ """
834
+ return VersionWithSource._from_py_version(*self._record.version)
835
+
836
+ @version.setter
837
+ def version(self, value: VersionWithSource) -> None:
838
+ self._record.version = (value._version, value._source)
839
+
840
+ @property
841
+ def python_site_packages_path(self) -> Optional[str]:
842
+ """
843
+ Optionally a path within the environment of the site-packages directory. This field is only
844
+ present for python interpreter packages.
845
+ This field was introduced with <https://github.com/conda/ceps/blob/main/cep-17.md>.
846
+
847
+ Examples
848
+ --------
849
+ ```python
850
+ >>> from rattler import PrefixRecord
851
+ >>> record = PrefixRecord.from_path(
852
+ ... "../test-data/conda-meta/python-3.11.9-h932a869_0_cpython.json"
853
+ ... )
854
+ >>> record.python_site_packages_path
855
+ 'lib/python3.11/site-packages'
856
+ >>>
857
+ ```
858
+ """
859
+ return self._record.python_site_packages_path
860
+
861
+ @python_site_packages_path.setter
862
+ def python_site_packages_path(self, value: Optional[str]) -> None:
863
+ """
864
+ Sets the optional path within the environment of the site-packages directory.
865
+ Examples
866
+ --------
867
+ ```python
868
+ >>> from rattler import PrefixRecord
869
+ >>> record = PrefixRecord.from_path(
870
+ ... "../test-data/conda-meta/libsqlite-3.40.0-hcfcfb64_0.json"
871
+ ... )
872
+ >>> record.python_site_packages_path
873
+ None
874
+ >>> record.python_site_packages_path = "lib/something"
875
+ >>> record.python_site_packages_path
876
+ 'lib/something'
877
+ >>>
878
+ ```
879
+ """
880
+ self._record.set_python_site_packages_path(value)
881
+
882
+ def __str__(self) -> str:
883
+ """
884
+ Returns the string representation of the PackageRecord.
885
+
886
+ Examples
887
+ --------
888
+ ```python
889
+ >>> record = PackageRecord.from_index_json(
890
+ ... "../test-data/conda-meta/pysocks-1.7.1-pyh0701188_6.json"
891
+ ... )
892
+ >>> str(record)
893
+ 'pysocks=1.7.1=pyh0701188_6'
894
+ >>>
895
+ ```
896
+ """
897
+ return self._record.as_str()
898
+
899
+ def __repr__(self) -> str:
900
+ """
901
+ Returns a representation of the PackageRecord.
902
+
903
+ Examples
904
+ --------
905
+ ```python
906
+ >>> record = PackageRecord.from_index_json(
907
+ ... "../test-data/conda-meta/pysocks-1.7.1-pyh0701188_6.json"
908
+ ... )
909
+ >>> record
910
+ PackageRecord("pysocks=1.7.1=pyh0701188_6")
911
+ >>>
912
+ ```
913
+ """
914
+ return f'PackageRecord("{self.__str__()}")'
915
+
916
+ def to_json(self) -> str:
917
+ """
918
+ Convert the PackageRecord to a JSON string.
919
+
920
+ Examples
921
+ --------
922
+ ```python
923
+ >>> import json
924
+ >>> record = PackageRecord.from_index_json(
925
+ ... "../test-data/conda-meta/pysocks-1.7.1-pyh0701188_6.json"
926
+ ... )
927
+ >>> json_data = record.to_json()
928
+ >>> isinstance(json_data, str)
929
+ True
930
+ >>> as_dict = json.loads(json_data)
931
+ >>> as_dict["name"]
932
+ 'pysocks'
933
+ >>> as_dict["version"]
934
+ '1.7.1'
935
+ >>>
936
+ ```
937
+ """
938
+ return self._record.to_json()