forked from release-engineering/pubtools-pulplib
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbase.py
141 lines (108 loc) · 4.32 KB
/
base.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import re
from ..common import PulpObject
from ..attr import pulp_attrib, PULP2_FIELD
from ...util import dict_put
from ... import compat_attr as attr
from ...schema import load_schema
UNIT_CLASSES = {}
def unit_type(pulp_type):
# decorator for Unit subclasses, registers against a
# particular value of type_id.
def decorate(klass):
UNIT_CLASSES[pulp_type] = klass
return klass
return decorate
def type_ids_for_class(unit_class):
# Given a concrete Unit subclass, returns those Pulp type id(s)
# which may be used to find/load an object of that class.
out = []
for pulp_type, klass in UNIT_CLASSES.items():
if klass is unit_class:
out.append(pulp_type)
return sorted(out)
def class_for_type_id(type_id):
# Inverse of type_ids_for_class: given a Pulp type ID,
# returns the corresponding Unit class, or None if there is no
# corresponding class.
return UNIT_CLASSES.get(type_id)
@attr.s(kw_only=True, frozen=True)
class Unit(PulpObject):
"""Represents a Pulp unit (a single piece of content).
.. versionadded:: 1.5.0
"""
_SCHEMA = load_schema("unit")
content_type_id = pulp_attrib(type=str, pulp_field="_content_type_id")
"""The type of this unit.
This value will match one of the content types returned by
:meth:`~pubtools.pulplib.Client.get_content_type_ids`.
"""
@classmethod
def from_data(cls, data):
# delegate to concrete subclass as needed
if cls is Unit:
type_id = data.get("_content_type_id")
for klass_type_id, klass in UNIT_CLASSES.items():
if klass_type_id == type_id:
return klass.from_data(data)
return super(Unit, cls).from_data(data)
@classmethod
def _from_task_data(cls, data):
# Like from_data, but massages the data from the format used in
# task units_successful, which is slightly different from content search.
unit_data = {}
unit_data["_content_type_id"] = data.get("type_id")
unit_data.update(data.get("unit_key") or {})
return cls.from_data(unit_data)
# A validator shared by various subclasses
def _check_sum(self, value, sumtype, length):
if value is None:
return
if not re.match(r"^[a-f0-9]{%s}$" % length, value):
raise ValueError("Not a valid %s: %s" % (sumtype, value))
@property
def _usermeta(self):
# Returns pulp_user_metadata dict for this unit.
out = {}
for field in self._usermeta_fields():
pulp_field = field.metadata.get(PULP2_FIELD)
python_value = getattr(self, field.name)
pulp_value = PulpObject._any_to_data(python_value)
dict_put(out, pulp_field, pulp_value)
return out.get("pulp_user_metadata") or {}
@classmethod
def _usermeta_fields(cls):
# Returns the subset of fields on this class which are stored under the
# pulp_user_metadata dict. In public API we refer to these as 'mutable' fields.
return [
fld
for fld in attr.fields(cls)
if fld.metadata.get(PULP2_FIELD).startswith("pulp_user_metadata.")
]
@classmethod
def _usermeta_from_kwargs(cls, **kwargs):
# Given kwargs mapping to mutable fields on this unit class, returns a dict
# of the form:
#
# {"pulp_user_metadata": {...serialized mutable fields}}
#
# ...suitable for merging into a 'metadata' dict on a content upload.
#
# If any of the kwargs do not map to a mutable field, an exception is raised.
#
fields = cls._usermeta_fields()
out = {}
for field in fields:
if not field.name in kwargs:
continue
pulp_field = field.metadata.get(PULP2_FIELD)
python_value = kwargs.pop(field.name)
pulp_value = PulpObject._any_to_data(python_value)
dict_put(out, pulp_field, pulp_value)
# Ensure that we have consumed all arguments; if not, that means the
# caller tried to set something we don't support.
remaining = sorted(kwargs.keys())
if remaining:
raise ValueError(
"Not mutable %s field(s): %s" % (cls.__name__, ", ".join(remaining))
)
return out