Source code for pubtools.pulplib._impl.model.unit.base

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)


[docs]@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