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]:
def
json_property_name( self, json_property: spdx_tools.spdx.jsonschema.json_property.JsonProperty) -> str:
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