omlish 0.0.0.dev221__py3-none-any.whl → 0.0.0.dev222__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
omlish/__about__.py CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev221'
2
- __revision__ = '2c4c1480e414176972a21bd34529721b39818ad8'
1
+ __version__ = '0.0.0.dev222'
2
+ __revision__ = '6f15b0f3bde72b207c93e58623da4bb444ff4003'
3
3
 
4
4
 
5
5
  #
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: omlish
3
- Version: 0.0.0.dev221
3
+ Version: 0.0.0.dev222
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -1,5 +1,5 @@
1
1
  omlish/.manifests.json,sha256=YGmAnUBszmosQQ_7Hh2wwtDiYdYZ4unNKYzOtALuels,7968
2
- omlish/__about__.py,sha256=u7q71WFVW-bTVdaEFl5j-jjsZImt5TKEkE4iaR6nat0,3380
2
+ omlish/__about__.py,sha256=qi9D_c4g4GLU0jkkVIC7_HYbKv2ZTrFCeMEs9eP0aPU,3380
3
3
  omlish/__init__.py,sha256=SsyiITTuK0v74XpKV8dqNaCmjOlan1JZKrHQv5rWKPA,253
4
4
  omlish/c3.py,sha256=ubu7lHwss5V4UznbejAI0qXhXahrU01MysuHOZI9C4U,8116
5
5
  omlish/cached.py,sha256=UI-XTFBwA6YXWJJJeBn-WkwBkfzDjLBBaZf4nIJA9y0,510
@@ -232,13 +232,6 @@ omlish/docker/hub.py,sha256=7LIuJGdA-N1Y1dmo50ynKM1KUTcnQM_5XbtPbdT_QLU,3940
232
232
  omlish/docker/manifests.py,sha256=LR4FpOGNUT3bZQ-gTjB6r_-1C3YiG30QvevZjrsVUQM,7068
233
233
  omlish/docker/portrelay.py,sha256=QlRoTnQXs5INguR7XOj1xH0gNdL9SUeZm5z45DUctXo,1222
234
234
  omlish/docker/timebomb.py,sha256=A_pgIDaXKsQwPiikrCTgIJl91gwYqkPGFY6j-Naq07Q,342
235
- omlish/docker/oci/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
236
- omlish/docker/oci/building.py,sha256=o62QmOfMEP84uLukk5nJ59bAmEKPvkNtgJqS-C8XS04,3544
237
- omlish/docker/oci/data.py,sha256=DTHtdnqdv1PTHKoW67WqBc7uPmTQtM_wcoCdt_h4sT0,3437
238
- omlish/docker/oci/datarefs.py,sha256=OUHhFy2TytGt2_YVzCOreOBBbFXmGHTpflUlj7oOz4A,2079
239
- omlish/docker/oci/loading.py,sha256=aJGa4bTXfiIV6K0aBMEaWY3u0fBCLWaKFvtW_YZOOCo,3287
240
- omlish/docker/oci/media.py,sha256=tn14DHNNhyFwrrprOdLx0es44aA9Eby8Z-sdrUMQe50,4898
241
- omlish/docker/oci/repositories.py,sha256=moN5mUrorcFMbvQ5jowTlspetSGO_aa3KuK6_EW1Qdk,1872
242
235
  omlish/formats/__init__.py,sha256=T0AG1gFnqQ5JiHN0UPQjQ-7g5tnxMIG-mgOvMYExYAM,21
243
236
  omlish/formats/cbor.py,sha256=o_Hbe4kthO9CeXK-FySrw0dHVlrdyTo2Y8PpGRDfZ3c,514
244
237
  omlish/formats/cloudpickle.py,sha256=16si4yUp_pAdDWGECAWf6HLA2PwSANGGgDoMLGZUem8,579
@@ -667,9 +660,9 @@ omlish/text/indent.py,sha256=YjtJEBYWuk8--b9JU_T6q4yxV85_TR7VEVr5ViRCFwk,1336
667
660
  omlish/text/minja.py,sha256=jZC-fp3Xuhx48ppqsf2Sf1pHbC0t8XBB7UpUUoOk2Qw,5751
668
661
  omlish/text/parts.py,sha256=7vPF1aTZdvLVYJ4EwBZVzRSy8XB3YqPd7JwEnNGGAOo,6495
669
662
  omlish/text/random.py,sha256=jNWpqiaKjKyTdMXC-pWAsSC10AAP-cmRRPVhm59ZWLk,194
670
- omlish-0.0.0.dev221.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
671
- omlish-0.0.0.dev221.dist-info/METADATA,sha256=d9X9JvDyZT6Ax6JfEeRU4w1pujLQB9OhgNv38axfOmM,4176
672
- omlish-0.0.0.dev221.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
673
- omlish-0.0.0.dev221.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
674
- omlish-0.0.0.dev221.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
675
- omlish-0.0.0.dev221.dist-info/RECORD,,
663
+ omlish-0.0.0.dev222.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
664
+ omlish-0.0.0.dev222.dist-info/METADATA,sha256=ij9bc4yyi5znMyYrn-XGpDpGokENDlDacgDAjOeyUFI,4176
665
+ omlish-0.0.0.dev222.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
666
+ omlish-0.0.0.dev222.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
667
+ omlish-0.0.0.dev222.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
668
+ omlish-0.0.0.dev222.dist-info/RECORD,,
File without changes
@@ -1,122 +0,0 @@
1
- # ruff: noqa: UP006 UP007
2
- # @omlish-lite
3
- import dataclasses as dc
4
- import typing as ta
5
-
6
- from ...lite.check import check
7
- from ...lite.json import json_dumps_compact
8
- from ...lite.marshal import marshal_obj
9
- from .data import OciDataclass
10
- from .data import OciImageConfig
11
- from .data import OciImageIndex
12
- from .data import OciImageLayer
13
- from .data import OciImageManifest
14
- from .datarefs import BytesOciDataRef
15
- from .datarefs import OciDataRef
16
- from .datarefs import OciDataRefInfo
17
- from .media import OCI_IMAGE_LAYER_KIND_MEDIA_TYPES
18
- from .media import OciMediaDataclass
19
- from .media import OciMediaDescriptor
20
- from .media import OciMediaImageConfig
21
- from .media import OciMediaImageIndex
22
- from .media import OciMediaImageManifest
23
-
24
-
25
- ##
26
-
27
-
28
- class OciRepositoryBuilder:
29
- def __init__(self) -> None:
30
- super().__init__()
31
-
32
- self._blobs: ta.Dict[str, OciDataRef] = {}
33
-
34
- def get_blobs(self) -> ta.Dict[str, OciDataRef]:
35
- return dict(self._blobs)
36
-
37
- def add_blob(
38
- self,
39
- r: OciDataRef,
40
- ri: ta.Optional[OciDataRefInfo] = None,
41
- ) -> None:
42
- if ri is None:
43
- ri = OciDataRefInfo(r)
44
- if ri.digest() in self._blobs:
45
- raise KeyError(ri.digest())
46
- self._blobs[ri.digest()] = r
47
-
48
- def marshal_media(self, obj: OciMediaDataclass) -> bytes:
49
- check.isinstance(obj, OciMediaDataclass)
50
- m = marshal_obj(obj)
51
- j = json_dumps_compact(m)
52
- b = j.encode('utf-8')
53
- return b
54
-
55
- def add_media(self, obj: OciMediaDataclass) -> OciMediaDescriptor:
56
- b = self.marshal_media(obj)
57
-
58
- r = BytesOciDataRef(b)
59
- ri = OciDataRefInfo(r)
60
- self.add_blob(r, ri)
61
-
62
- return OciMediaDescriptor(
63
- media_type=getattr(obj, 'media_type'),
64
- digest=ri.digest(),
65
- size=ri.size(),
66
- )
67
-
68
- def to_media(self, obj: OciDataclass) -> ta.Union[OciMediaDataclass, OciMediaDescriptor]:
69
- def make_kw(*exclude):
70
- return {
71
- a: v
72
- for f in dc.fields(obj)
73
- if (a := f.name) not in exclude
74
- for v in [getattr(obj, a)]
75
- if v is not None
76
- }
77
-
78
- if isinstance(obj, OciImageIndex):
79
- return OciMediaImageIndex(
80
- **make_kw('manifests'),
81
- manifests=[
82
- self.add_data(m)
83
- for m in obj.manifests
84
- ],
85
- )
86
-
87
- elif isinstance(obj, OciImageManifest):
88
- return OciMediaImageManifest(
89
- **make_kw('config', 'layers'),
90
- config=self.add_data(obj.config),
91
- layers=[
92
- self.add_data(l)
93
- for l in obj.layers
94
- ],
95
- )
96
-
97
- elif isinstance(obj, OciImageLayer):
98
- ri = OciDataRefInfo(obj.data)
99
- self.add_blob(obj.data, ri)
100
- return OciMediaDescriptor(
101
- media_type=OCI_IMAGE_LAYER_KIND_MEDIA_TYPES[obj.kind],
102
- digest=ri.digest(),
103
- size=ri.size(),
104
- )
105
-
106
- elif isinstance(obj, OciImageConfig):
107
- return OciMediaImageConfig(**make_kw())
108
-
109
- else:
110
- raise TypeError(obj)
111
-
112
- def add_data(self, obj: OciDataclass) -> OciMediaDescriptor:
113
- ret = self.to_media(obj)
114
-
115
- if isinstance(ret, OciMediaDataclass):
116
- return self.add_media(ret)
117
-
118
- elif isinstance(ret, OciMediaDescriptor):
119
- return ret
120
-
121
- else:
122
- raise TypeError(ret)
omlish/docker/oci/data.py DELETED
@@ -1,125 +0,0 @@
1
- # ruff: noqa: UP006 UP007
2
- # @omlish-lite
3
- import abc
4
- import dataclasses as dc
5
- import enum
6
- import typing as ta
7
-
8
- from ...lite.marshal import OBJ_MARSHALER_FIELD_KEY
9
- from ...lite.marshal import OBJ_MARSHALER_OMIT_IF_NONE
10
- from .datarefs import OciDataRef
11
-
12
-
13
- ##
14
-
15
-
16
- @dc.dataclass()
17
- class OciDataclass(abc.ABC): # noqa
18
- pass
19
-
20
-
21
- ##
22
-
23
-
24
- @dc.dataclass()
25
- class OciImageIndex(OciDataclass):
26
- manifests: ta.List[ta.Union['OciImageIndex', 'OciImageManifest']]
27
-
28
- annotations: ta.Optional[ta.Dict[str, str]] = None
29
-
30
-
31
- #
32
-
33
-
34
- @dc.dataclass()
35
- class OciImageManifest(OciDataclass):
36
- config: 'OciImageConfig'
37
-
38
- layers: ta.List['OciImageLayer']
39
-
40
-
41
- #
42
-
43
-
44
- @dc.dataclass()
45
- class OciImageLayer(OciDataclass):
46
- class Kind(enum.Enum):
47
- TAR = enum.auto()
48
- TAR_GZIP = enum.auto()
49
- TAR_ZSTD = enum.auto()
50
-
51
- kind: Kind
52
-
53
- data: OciDataRef
54
-
55
-
56
- #
57
-
58
-
59
- @dc.dataclass()
60
- class OciImageConfig(OciDataclass):
61
- """https://github.com/opencontainers/image-spec/blob/92353b0bee778725c617e7d57317b568a7796bd0/config.md"""
62
-
63
- architecture: str
64
- os: str
65
-
66
- @dc.dataclass()
67
- class RootFs:
68
- type: str
69
- diff_ids: ta.List[str]
70
-
71
- rootfs: RootFs
72
-
73
- #
74
-
75
- created: ta.Optional[str] = dc.field(default=None, metadata={OBJ_MARSHALER_OMIT_IF_NONE: True})
76
- author: ta.Optional[str] = dc.field(default=None, metadata={OBJ_MARSHALER_OMIT_IF_NONE: True})
77
- os_version: ta.Optional[str] = dc.field(default=None, metadata={OBJ_MARSHALER_FIELD_KEY: 'os.version', OBJ_MARSHALER_OMIT_IF_NONE: True}) # noqa
78
- os_features: ta.Optional[ta.List[str]] = dc.field(default=None, metadata={OBJ_MARSHALER_FIELD_KEY: 'os.features', OBJ_MARSHALER_OMIT_IF_NONE: True}) # noqa
79
- variant: ta.Optional[str] = dc.field(default=None, metadata={OBJ_MARSHALER_OMIT_IF_NONE: True})
80
-
81
- """
82
- config object, OPTIONAL
83
- User string, OPTIONAL
84
- ExposedPorts object, OPTIONAL
85
- Env array of strings, OPTIONAL
86
- Entrypoint array of strings, OPTIONAL
87
- Cmd array of strings, OPTIONAL
88
- Volumes object, OPTIONAL
89
- WorkingDir string, OPTIONAL
90
- Labels object, OPTIONAL
91
- StopSignal string, OPTIONAL
92
- ArgsEscaped boolean, OPTIONAL
93
- Memory integer, OPTIONAL
94
- MemorySwap integer, OPTIONAL
95
- CpuShares integer, OPTIONAL
96
- Healthcheck object, OPTIONAL
97
- """
98
- config: ta.Optional[ta.Dict[str, ta.Any]] = dc.field(default=None, metadata={OBJ_MARSHALER_OMIT_IF_NONE: True})
99
-
100
- @dc.dataclass()
101
- class History:
102
- created: ta.Optional[str] = dc.field(default=None, metadata={OBJ_MARSHALER_OMIT_IF_NONE: True})
103
- author: ta.Optional[str] = dc.field(default=None, metadata={OBJ_MARSHALER_OMIT_IF_NONE: True})
104
- created_by: ta.Optional[str] = dc.field(default=None, metadata={OBJ_MARSHALER_OMIT_IF_NONE: True})
105
- comment: ta.Optional[str] = dc.field(default=None, metadata={OBJ_MARSHALER_OMIT_IF_NONE: True})
106
- empty_layer: ta.Optional[bool] = dc.field(default=None, metadata={OBJ_MARSHALER_OMIT_IF_NONE: True})
107
-
108
- history: ta.Optional[ta.List[History]] = dc.field(default=None, metadata={OBJ_MARSHALER_OMIT_IF_NONE: True})
109
-
110
-
111
- ##
112
-
113
-
114
- def is_empty_oci_dataclass(obj: OciDataclass) -> bool:
115
- if not isinstance(obj, OciDataclass):
116
- raise TypeError(obj)
117
-
118
- elif isinstance(obj, OciImageIndex):
119
- return not obj.manifests
120
-
121
- elif isinstance(obj, OciImageManifest):
122
- return not obj.layers
123
-
124
- else:
125
- return False
@@ -1,98 +0,0 @@
1
- # ruff: noqa: UP006 UP007
2
- # @omlish-lite
3
- import abc
4
- import dataclasses as dc
5
- import hashlib
6
- import io
7
- import os.path
8
- import shutil
9
- import typing as ta
10
-
11
- from omlish.lite.cached import cached_nullary
12
-
13
-
14
- ##
15
-
16
-
17
- @dc.dataclass(frozen=True)
18
- class OciDataRef(abc.ABC): # noqa
19
- pass
20
-
21
-
22
- @dc.dataclass(frozen=True)
23
- class BytesOciDataRef(OciDataRef):
24
- data: bytes
25
-
26
-
27
- @dc.dataclass(frozen=True)
28
- class FileOciDataRef(OciDataRef):
29
- path: str
30
-
31
-
32
- ##
33
-
34
-
35
- @dc.dataclass(frozen=True)
36
- class OciDataRefInfo:
37
- data: OciDataRef
38
-
39
- @cached_nullary
40
- def sha256(self) -> str:
41
- if isinstance(self.data, FileOciDataRef):
42
- with open(self.data.path, 'rb') as f:
43
- return hashlib.file_digest(f, 'sha256').hexdigest() # noqa
44
-
45
- elif isinstance(self.data, BytesOciDataRef):
46
- return hashlib.sha256(self.data.data).hexdigest()
47
-
48
- else:
49
- raise TypeError(self.data)
50
-
51
- @cached_nullary
52
- def digest(self) -> str:
53
- return f'sha256:{self.sha256()}'
54
-
55
- @cached_nullary
56
- def size(self) -> int:
57
- if isinstance(self.data, FileOciDataRef):
58
- return os.path.getsize(self.data.path)
59
-
60
- elif isinstance(self.data, BytesOciDataRef):
61
- return len(self.data.data)
62
-
63
- else:
64
- raise TypeError(self.data)
65
-
66
-
67
- def write_oci_data_ref_to_file(
68
- data: OciDataRef,
69
- dst: str,
70
- *,
71
- symlink: bool = False,
72
- ) -> None:
73
- if isinstance(data, FileOciDataRef):
74
- if symlink:
75
- os.symlink(
76
- os.path.relpath(data.path, os.path.dirname(dst)),
77
- dst,
78
- )
79
- else:
80
- shutil.copyfile(data.path, dst)
81
-
82
- elif isinstance(data, BytesOciDataRef):
83
- with open(dst, 'wb') as f:
84
- f.write(data.data)
85
-
86
- else:
87
- raise TypeError(data)
88
-
89
-
90
- def open_oci_data_ref(data: OciDataRef) -> ta.BinaryIO:
91
- if isinstance(data, FileOciDataRef):
92
- return open(data.path, 'rb')
93
-
94
- elif isinstance(data, BytesOciDataRef):
95
- return io.BytesIO(data.data)
96
-
97
- else:
98
- raise TypeError(data)
@@ -1,120 +0,0 @@
1
- # ruff: noqa: UP006 UP007
2
- # @omlish-lite
3
- import dataclasses as dc
4
- import json
5
- import typing as ta
6
-
7
- from ...lite.check import check
8
- from .data import OciImageConfig
9
- from .data import OciImageIndex
10
- from .data import OciImageLayer
11
- from .data import OciImageManifest
12
- from .data import is_empty_oci_dataclass
13
- from .media import OCI_IMAGE_LAYER_KIND_MEDIA_TYPES_
14
- from .media import OCI_MEDIA_FIELDS
15
- from .media import OciMediaDescriptor
16
- from .media import OciMediaImageConfig
17
- from .media import OciMediaImageIndex
18
- from .media import OciMediaImageManifest
19
- from .media import unmarshal_oci_media_dataclass
20
- from .repositories import OciRepository
21
-
22
-
23
- T = ta.TypeVar('T')
24
-
25
-
26
- ##
27
-
28
-
29
- class OciRepositoryLoader:
30
- def __init__(
31
- self,
32
- repo: OciRepository,
33
- ) -> None:
34
- super().__init__()
35
-
36
- self._repo = repo
37
-
38
- #
39
-
40
- def load_object(
41
- self,
42
- data: bytes,
43
- cls: ta.Type[T] = object, # type: ignore[assignment]
44
- *,
45
- media_type: ta.Optional[str] = None,
46
- ) -> T:
47
- text = data.decode('utf-8')
48
- dct = json.loads(text)
49
- obj = unmarshal_oci_media_dataclass(
50
- dct,
51
- media_type=media_type,
52
- )
53
- return check.isinstance(obj, cls)
54
-
55
- def read_object(
56
- self,
57
- digest: str,
58
- cls: ta.Type[T] = object, # type: ignore[assignment]
59
- *,
60
- media_type: ta.Optional[str] = None,
61
- ) -> T:
62
- data = self._repo.read_blob(digest)
63
- return self.load_object(
64
- data,
65
- cls,
66
- media_type=media_type,
67
- )
68
-
69
- def read_descriptor(
70
- self,
71
- desc: OciMediaDescriptor,
72
- cls: ta.Type[T] = object, # type: ignore[assignment]
73
- ) -> ta.Any:
74
- return self.read_object(
75
- desc.digest,
76
- cls,
77
- media_type=desc.media_type,
78
- )
79
-
80
- #
81
-
82
- def from_media(self, obj: ta.Any) -> ta.Any:
83
- def make_kw(*exclude):
84
- return {
85
- a: getattr(obj, a)
86
- for f in dc.fields(obj)
87
- if (a := f.name) not in OCI_MEDIA_FIELDS
88
- and a not in exclude
89
- }
90
-
91
- if isinstance(obj, OciMediaImageConfig):
92
- return OciImageConfig(**make_kw())
93
-
94
- elif isinstance(obj, OciMediaImageManifest):
95
- return OciImageManifest(
96
- **make_kw('config', 'layers'),
97
- config=self.from_media(self.read_descriptor(obj.config)),
98
- layers=[
99
- OciImageLayer(
100
- kind=lk,
101
- data=self._repo.ref_blob(l.digest),
102
- )
103
- for l in obj.layers
104
- if (lk := OCI_IMAGE_LAYER_KIND_MEDIA_TYPES_.get(l.media_type)) is not None
105
- ],
106
- )
107
-
108
- elif isinstance(obj, OciMediaImageIndex):
109
- return OciImageIndex(
110
- **make_kw('manifests'),
111
- manifests=[
112
- fm
113
- for m in obj.manifests
114
- for fm in [self.from_media(self.read_descriptor(m))]
115
- if not is_empty_oci_dataclass(fm)
116
- ],
117
- )
118
-
119
- else:
120
- raise TypeError(obj)
@@ -1,154 +0,0 @@
1
- # ruff: noqa: UP006 UP007
2
- # @omlish-lite
3
- import abc
4
- import dataclasses as dc
5
- import typing as ta
6
-
7
- from ...lite.check import check
8
- from ...lite.marshal import OBJ_MARSHALER_FIELD_KEY
9
- from ...lite.marshal import OBJ_MARSHALER_OMIT_IF_NONE
10
- from ...lite.marshal import unmarshal_obj
11
- from .data import OciImageConfig
12
- from .data import OciImageLayer
13
-
14
-
15
- ##
16
-
17
-
18
- OCI_MEDIA_FIELDS: ta.Collection[str] = frozenset([
19
- 'schema_version',
20
- 'media_type',
21
- ])
22
-
23
-
24
- @dc.dataclass()
25
- class OciMediaDataclass(abc.ABC): # noqa
26
- SCHEMA_VERSION: ta.ClassVar[int]
27
- MEDIA_TYPE: ta.ClassVar[str]
28
-
29
- def __init_subclass__(cls, **kwargs: ta.Any) -> None:
30
- super().__init_subclass__(**kwargs)
31
- for a in OCI_MEDIA_FIELDS:
32
- check.in_(a, cls.__dict__)
33
-
34
-
35
- _REGISTERED_OCI_MEDIA_DATACLASSES: ta.Dict[str, ta.Type[OciMediaDataclass]] = {}
36
-
37
-
38
- def _register_oci_media_dataclass(cls):
39
- check.issubclass(cls, OciMediaDataclass)
40
- check.arg(dc.is_dataclass(cls))
41
- mt = check.non_empty_str(cls.__dict__['MEDIA_TYPE'])
42
- check.not_in(mt, _REGISTERED_OCI_MEDIA_DATACLASSES)
43
- _REGISTERED_OCI_MEDIA_DATACLASSES[mt] = cls
44
- return cls
45
-
46
-
47
- def get_registered_oci_media_dataclass(media_type: str) -> ta.Optional[ta.Type[OciMediaDataclass]]:
48
- return _REGISTERED_OCI_MEDIA_DATACLASSES.get(media_type)
49
-
50
-
51
- def unmarshal_oci_media_dataclass(
52
- dct: ta.Mapping[str, ta.Any],
53
- *,
54
- media_type: ta.Optional[str] = None,
55
- ) -> ta.Any:
56
- if media_type is None:
57
- media_type = check.non_empty_str(dct['mediaType'])
58
- cls = _REGISTERED_OCI_MEDIA_DATACLASSES[media_type]
59
- return unmarshal_obj(dct, cls)
60
-
61
-
62
- ##
63
-
64
-
65
- @dc.dataclass()
66
- class OciMediaDescriptor:
67
- """https://github.com/opencontainers/image-spec/blob/92353b0bee778725c617e7d57317b568a7796bd0/descriptor.md#properties""" # noqa
68
-
69
- media_type: str = dc.field(metadata={OBJ_MARSHALER_FIELD_KEY: 'mediaType'})
70
- digest: str
71
- size: int
72
-
73
- #
74
-
75
- urls: ta.Optional[ta.Sequence[str]] = dc.field(default=None, metadata={OBJ_MARSHALER_OMIT_IF_NONE: True})
76
- annotations: ta.Optional[ta.Mapping[str, str]] = dc.field(default=None, metadata={OBJ_MARSHALER_OMIT_IF_NONE: True}) # noqa
77
- data: ta.Optional[str] = dc.field(default=None, metadata={OBJ_MARSHALER_OMIT_IF_NONE: True})
78
- artifact_type: ta.Optional[str] = dc.field(default=None, metadata={OBJ_MARSHALER_FIELD_KEY: 'artifactType', OBJ_MARSHALER_OMIT_IF_NONE: True}) # noqa
79
-
80
- #
81
-
82
- platform: ta.Optional[ta.Mapping[str, ta.Any]] = dc.field(default=None, metadata={OBJ_MARSHALER_OMIT_IF_NONE: True}) # noqa
83
-
84
-
85
- ##
86
-
87
-
88
- @_register_oci_media_dataclass
89
- @dc.dataclass()
90
- class OciMediaImageIndex(OciMediaDataclass):
91
- """https://github.com/opencontainers/image-spec/blob/92353b0bee778725c617e7d57317b568a7796bd0/image-index.md"""
92
-
93
- manifests: ta.Sequence[OciMediaDescriptor] # -> OciMediaImageIndex | OciMediaImageManifest
94
-
95
- #
96
-
97
- annotations: ta.Optional[ta.Mapping[str, str]] = dc.field(default=None, metadata={OBJ_MARSHALER_OMIT_IF_NONE: True}) # noqa
98
-
99
- #
100
-
101
- SCHEMA_VERSION: ta.ClassVar[int] = 2
102
- schema_version: int = dc.field(default=SCHEMA_VERSION, metadata={OBJ_MARSHALER_FIELD_KEY: 'schemaVersion'})
103
-
104
- MEDIA_TYPE: ta.ClassVar[str] = 'application/vnd.oci.image.index.v1+json'
105
- media_type: str = dc.field(default=MEDIA_TYPE, metadata={OBJ_MARSHALER_FIELD_KEY: 'mediaType'})
106
-
107
-
108
- #
109
-
110
-
111
- @_register_oci_media_dataclass
112
- @dc.dataclass()
113
- class OciMediaImageManifest(OciMediaDataclass):
114
- """https://github.com/opencontainers/image-spec/blob/92353b0bee778725c617e7d57317b568a7796bd0/manifest.md"""
115
-
116
- config: OciMediaDescriptor # -> OciMediaImageConfig
117
-
118
- layers: ta.Sequence[OciMediaDescriptor]
119
-
120
- #
121
-
122
- SCHEMA_VERSION: ta.ClassVar[int] = 2
123
- schema_version: int = dc.field(default=SCHEMA_VERSION, metadata={OBJ_MARSHALER_FIELD_KEY: 'schemaVersion'})
124
-
125
- MEDIA_TYPE: ta.ClassVar[str] = 'application/vnd.oci.image.manifest.v1+json'
126
- media_type: str = dc.field(default=MEDIA_TYPE, metadata={OBJ_MARSHALER_FIELD_KEY: 'mediaType'})
127
-
128
-
129
- #
130
-
131
-
132
- OCI_IMAGE_LAYER_KIND_MEDIA_TYPES: ta.Mapping[OciImageLayer.Kind, str] = {
133
- OciImageLayer.Kind.TAR: 'application/vnd.oci.image.layer.v1.tar',
134
- OciImageLayer.Kind.TAR_GZIP: 'application/vnd.oci.image.layer.v1.tar+gzip',
135
- OciImageLayer.Kind.TAR_ZSTD: 'application/vnd.oci.image.layer.v1.tar+zstd',
136
- }
137
-
138
- OCI_IMAGE_LAYER_KIND_MEDIA_TYPES_: ta.Mapping[str, OciImageLayer.Kind] = {
139
- v: k
140
- for k, v in OCI_IMAGE_LAYER_KIND_MEDIA_TYPES.items()
141
- }
142
-
143
-
144
- #
145
-
146
-
147
- @_register_oci_media_dataclass
148
- @dc.dataclass()
149
- class OciMediaImageConfig(OciImageConfig, OciMediaDataclass):
150
- SCHEMA_VERSION: ta.ClassVar[int] = 2
151
- schema_version: int = dc.field(default=SCHEMA_VERSION, metadata={OBJ_MARSHALER_FIELD_KEY: 'schemaVersion'})
152
-
153
- MEDIA_TYPE: ta.ClassVar[str] = 'application/vnd.oci.image.config.v1+json'
154
- media_type: str = dc.field(default=MEDIA_TYPE, metadata={OBJ_MARSHALER_FIELD_KEY: 'mediaType'})
@@ -1,72 +0,0 @@
1
- # ruff: noqa: UP006 UP007
2
- # @omlish-lite
3
- import abc
4
- import os.path
5
- import typing as ta
6
-
7
- from ...lite.check import check
8
- from ...os.paths import is_path_in_dir
9
- from .datarefs import BytesOciDataRef
10
- from .datarefs import FileOciDataRef
11
- from .datarefs import OciDataRef
12
-
13
-
14
- ##
15
-
16
-
17
- class OciRepository(abc.ABC):
18
- @abc.abstractmethod
19
- def read_blob(self, digest: str) -> bytes:
20
- raise NotImplementedError
21
-
22
- @abc.abstractmethod
23
- def ref_blob(self, digest: str) -> OciDataRef:
24
- raise NotImplementedError
25
-
26
-
27
- #
28
-
29
-
30
- class DirectoryOciRepository(OciRepository):
31
- def __init__(self, data_dir: str) -> None:
32
- super().__init__()
33
-
34
- self._data_dir = check.non_empty_str(data_dir)
35
-
36
- def read_file(self, path: str) -> bytes:
37
- full_path = os.path.join(self._data_dir, path)
38
- check.arg(is_path_in_dir(self._data_dir, full_path))
39
- with open(full_path, 'rb') as f:
40
- return f.read()
41
-
42
- def blob_path(self, digest: str) -> str:
43
- scheme, value = digest.split(':')
44
- return os.path.join('blobs', scheme, value)
45
-
46
- def blob_full_path(self, digest: str) -> str:
47
- path = self.blob_path(digest)
48
- full_path = os.path.join(self._data_dir, path)
49
- check.arg(is_path_in_dir(self._data_dir, full_path))
50
- return full_path
51
-
52
- def read_blob(self, digest: str) -> bytes:
53
- return self.read_file(self.blob_path(digest))
54
-
55
- def ref_blob(self, digest: str) -> OciDataRef:
56
- return FileOciDataRef(self.blob_full_path(digest))
57
-
58
-
59
- #
60
-
61
-
62
- class DictionaryOciRepository(OciRepository):
63
- def __init__(self, blobs: ta.Mapping[str, bytes]) -> None:
64
- super().__init__()
65
-
66
- self._blobs = blobs
67
-
68
- def read_blob(self, digest: str) -> bytes:
69
- return self._blobs[digest]
70
-
71
- def ref_blob(self, digest: str) -> OciDataRef:
72
- return BytesOciDataRef(self._blobs[digest])