spdx_tools.spdx.jsonschema.converter

 1# SPDX-FileCopyrightText: 2022 spdx contributors
 2#
 3# SPDX-License-Identifier: Apache-2.0
 4from abc import ABC, abstractmethod
 5
 6from beartype.typing import Any, Dict, Generic, Type, TypeVar
 7
 8from spdx_tools.spdx.casing_tools import snake_case_to_camel_case
 9from spdx_tools.spdx.jsonschema.json_property import JsonProperty
10from spdx_tools.spdx.model import Document
11
12MISSING_IMPLEMENTATION_MESSAGE = "Must be implemented"
13
14T = TypeVar("T")
15
16
17class TypedConverter(ABC, Generic[T]):
18    """
19    Base class for all converters between an instance of the tools-python data model and the corresponding dictionary
20    representation, following the json schema. The generic type T is the type according to the data model.
21    Each converter has several methods:
22    - get_json_type and get_data_model_type: return the data model type and the corresponding JsonProperty subclass.
23    These methods are abstract in the base class and need to be implemented in subclasses.
24    - json_property_name: converts an enum value of a JsonProperty subclass to the corresponding property name in the
25    json schema. The default implementation simply converts from snake case to camel case. Can be overridden in case
26    of exceptions like "SPDXID".
27    - convert: converts an instance of type T (one of the data model types) to a dictionary representation. In some
28    cases, the full document is required (see below). The logic should be generic for all types.
29    - requires_full_document: indicates whether the full document is required for conversion. Returns False by
30    default, can be overridden as needed for specific types.
31    - _get_property_value: Retrieves the value of a specific json property from the data model instance. In some
32    cases, the full document is required.
33    """
34
35    @abstractmethod
36    def _get_property_value(self, instance: T, json_property: JsonProperty, document: Document = None) -> Any:
37        raise NotImplementedError(MISSING_IMPLEMENTATION_MESSAGE)
38
39    @abstractmethod
40    def get_json_type(self) -> Type[JsonProperty]:
41        raise NotImplementedError(MISSING_IMPLEMENTATION_MESSAGE)
42
43    @abstractmethod
44    def get_data_model_type(self) -> Type[T]:
45        raise NotImplementedError(MISSING_IMPLEMENTATION_MESSAGE)
46
47    def json_property_name(self, json_property: JsonProperty) -> str:
48        return snake_case_to_camel_case(json_property.name)
49
50    def requires_full_document(self) -> bool:
51        return False
52
53    def convert(self, instance: T, document: Document = None) -> Dict:
54        if not isinstance(instance, self.get_data_model_type()):
55            raise TypeError(
56                f"Converter of type {self.__class__} can only convert objects of type "
57                f"{self.get_data_model_type()}. Received {type(instance)} instead."
58            )
59        if self.requires_full_document() and not document:
60            raise ValueError(f"Converter of type {self.__class__} requires the full document")
61
62        result = {}
63        for property_name in self.get_json_type():
64            property_value = self._get_property_value(instance, property_name, document)
65            if property_value is None:
66                continue
67            result[self.json_property_name(property_name)] = property_value
68        return result
MISSING_IMPLEMENTATION_MESSAGE = 'Must be implemented'
class TypedConverter(abc.ABC, typing.Generic[~T]):
18class TypedConverter(ABC, Generic[T]):
19    """
20    Base class for all converters between an instance of the tools-python data model and the corresponding dictionary
21    representation, following the json schema. The generic type T is the type according to the data model.
22    Each converter has several methods:
23    - get_json_type and get_data_model_type: return the data model type and the corresponding JsonProperty subclass.
24    These methods are abstract in the base class and need to be implemented in subclasses.
25    - json_property_name: converts an enum value of a JsonProperty subclass to the corresponding property name in the
26    json schema. The default implementation simply converts from snake case to camel case. Can be overridden in case
27    of exceptions like "SPDXID".
28    - convert: converts an instance of type T (one of the data model types) to a dictionary representation. In some
29    cases, the full document is required (see below). The logic should be generic for all types.
30    - requires_full_document: indicates whether the full document is required for conversion. Returns False by
31    default, can be overridden as needed for specific types.
32    - _get_property_value: Retrieves the value of a specific json property from the data model instance. In some
33    cases, the full document is required.
34    """
35
36    @abstractmethod
37    def _get_property_value(self, instance: T, json_property: JsonProperty, document: Document = None) -> Any:
38        raise NotImplementedError(MISSING_IMPLEMENTATION_MESSAGE)
39
40    @abstractmethod
41    def get_json_type(self) -> Type[JsonProperty]:
42        raise NotImplementedError(MISSING_IMPLEMENTATION_MESSAGE)
43
44    @abstractmethod
45    def get_data_model_type(self) -> Type[T]:
46        raise NotImplementedError(MISSING_IMPLEMENTATION_MESSAGE)
47
48    def json_property_name(self, json_property: JsonProperty) -> str:
49        return snake_case_to_camel_case(json_property.name)
50
51    def requires_full_document(self) -> bool:
52        return False
53
54    def convert(self, instance: T, document: Document = None) -> Dict:
55        if not isinstance(instance, self.get_data_model_type()):
56            raise TypeError(
57                f"Converter of type {self.__class__} can only convert objects of type "
58                f"{self.get_data_model_type()}. Received {type(instance)} instead."
59            )
60        if self.requires_full_document() and not document:
61            raise ValueError(f"Converter of type {self.__class__} requires the full document")
62
63        result = {}
64        for property_name in self.get_json_type():
65            property_value = self._get_property_value(instance, property_name, document)
66            if property_value is None:
67                continue
68            result[self.json_property_name(property_name)] = property_value
69        return result

Base class for all converters between an instance of the tools-python data model and the corresponding dictionary representation, following the json schema. The generic type T is the type according to the data model. Each converter has several methods:

  • get_json_type and get_data_model_type: return the data model type and the corresponding JsonProperty subclass. These methods are abstract in the base class and need to be implemented in subclasses.
  • json_property_name: converts an enum value of a JsonProperty subclass to the corresponding property name in the json schema. The default implementation simply converts from snake case to camel case. Can be overridden in case of exceptions like "SPDXID".
  • convert: converts an instance of type T (one of the data model types) to a dictionary representation. In some cases, the full document is required (see below). The logic should be generic for all types.
  • requires_full_document: indicates whether the full document is required for conversion. Returns False by default, can be overridden as needed for specific types.
  • _get_property_value: Retrieves the value of a specific json property from the data model instance. In some cases, the full document is required.
@abstractmethod
def get_json_type(self) -> type[spdx_tools.spdx.jsonschema.json_property.JsonProperty]:
40    @abstractmethod
41    def get_json_type(self) -> Type[JsonProperty]:
42        raise NotImplementedError(MISSING_IMPLEMENTATION_MESSAGE)
@abstractmethod
def get_data_model_type(self) -> type[~T]:
44    @abstractmethod
45    def get_data_model_type(self) -> Type[T]:
46        raise NotImplementedError(MISSING_IMPLEMENTATION_MESSAGE)
def json_property_name( self, json_property: spdx_tools.spdx.jsonschema.json_property.JsonProperty) -> str:
48    def json_property_name(self, json_property: JsonProperty) -> str:
49        return snake_case_to_camel_case(json_property.name)
def requires_full_document(self) -> bool:
51    def requires_full_document(self) -> bool:
52        return False
def convert( self, instance: ~T, document: spdx_tools.spdx.model.document.Document = None) -> dict:
54    def convert(self, instance: T, document: Document = None) -> Dict:
55        if not isinstance(instance, self.get_data_model_type()):
56            raise TypeError(
57                f"Converter of type {self.__class__} can only convert objects of type "
58                f"{self.get_data_model_type()}. Received {type(instance)} instead."
59            )
60        if self.requires_full_document() and not document:
61            raise ValueError(f"Converter of type {self.__class__} requires the full document")
62
63        result = {}
64        for property_name in self.get_json_type():
65            property_value = self._get_property_value(instance, property_name, document)
66            if property_value is None:
67                continue
68            result[self.json_property_name(property_name)] = property_value
69        return result