spdx_tools.spdx.parser.jsonlikedict.relationship_parser

  1# SPDX-FileCopyrightText: 2022 spdx contributors
  2#
  3# SPDX-License-Identifier: Apache-2.0
  4from beartype.typing import Dict, List, Optional
  5
  6from spdx_tools.common.typing.constructor_type_errors import ConstructorTypeErrors
  7from spdx_tools.spdx.model import Relationship, RelationshipType
  8from spdx_tools.spdx.parser.error import SPDXParsingError
  9from spdx_tools.spdx.parser.jsonlikedict.dict_parsing_functions import (
 10    delete_duplicates_from_list,
 11    json_str_to_enum_name,
 12    parse_field_or_log_error,
 13    parse_field_or_no_assertion_or_none,
 14)
 15from spdx_tools.spdx.parser.logger import Logger
 16from spdx_tools.spdx.parser.parsing_functions import (
 17    construct_or_raise_parsing_error,
 18    raise_parsing_error_if_logger_has_messages,
 19)
 20
 21
 22class RelationshipParser:
 23    logger: Logger
 24
 25    def __init__(self):
 26        self.logger = Logger()
 27
 28    def parse_all_relationships(self, input_doc_dict: Dict) -> List[Relationship]:
 29        relationships = []
 30        relationship_dicts: List[Dict] = input_doc_dict.get("relationships", [])
 31        relationships.extend(
 32            parse_field_or_log_error(self.logger, relationship_dicts, self.parse_relationship, [], True)
 33        )
 34
 35        document_describes: List[str] = delete_duplicates_from_list(input_doc_dict.get("documentDescribes", []))
 36        doc_spdx_id: Optional[str] = input_doc_dict.get("SPDXID")
 37
 38        existing_relationships_without_comments: List[Relationship] = self.get_all_relationships_without_comments(
 39            relationships
 40        )
 41        relationships.extend(
 42            parse_field_or_log_error(
 43                self.logger,
 44                document_describes,
 45                lambda x: self.parse_document_describes(
 46                    doc_spdx_id=doc_spdx_id,
 47                    described_spdx_ids=x,
 48                    existing_relationships=existing_relationships_without_comments,
 49                ),
 50                [],
 51            )
 52        )
 53
 54        package_dicts: List[Dict] = input_doc_dict.get("packages", [])
 55        existing_relationships_without_comments: List[Relationship] = self.get_all_relationships_without_comments(
 56            relationships
 57        )
 58
 59        relationships.extend(
 60            parse_field_or_log_error(
 61                self.logger,
 62                package_dicts,
 63                lambda x: self.parse_has_files(
 64                    package_dicts=x, existing_relationships=existing_relationships_without_comments
 65                ),
 66                [],
 67            )
 68        )
 69
 70        file_dicts: List[Dict] = input_doc_dict.get("files", [])
 71
 72        # not implemented yet: deal with deprecated fields in file:
 73        # https://github.com/spdx/tools-python/issues/294 & https://github.com/spdx/tools-python/issues/387
 74        _ = self.parse_artifact_of(file_dicts=file_dicts)
 75        _ = self.parse_file_dependencies(file_dicts=file_dicts)
 76
 77        raise_parsing_error_if_logger_has_messages(self.logger)
 78
 79        return relationships
 80
 81    def parse_relationship(self, relationship_dict: Dict) -> Relationship:
 82        logger = Logger()
 83        spdx_element_id: Optional[str] = relationship_dict.get("spdxElementId")
 84        related_spdx_element: Optional[str] = parse_field_or_no_assertion_or_none(
 85            relationship_dict.get("relatedSpdxElement")
 86        )
 87        relationship_type: Optional[RelationshipType] = parse_field_or_log_error(
 88            logger, relationship_dict.get("relationshipType"), self.parse_relationship_type
 89        )
 90        relationship_comment: Optional[str] = relationship_dict.get("comment")
 91        raise_parsing_error_if_logger_has_messages(logger, "Relationship")
 92
 93        relationship = construct_or_raise_parsing_error(
 94            Relationship,
 95            dict(
 96                spdx_element_id=spdx_element_id,
 97                relationship_type=relationship_type,
 98                related_spdx_element_id=related_spdx_element,
 99                comment=relationship_comment,
100            ),
101        )
102        return relationship
103
104    @staticmethod
105    def parse_relationship_type(relationship_type_str: str) -> RelationshipType:
106        try:
107            relationship_type = RelationshipType[json_str_to_enum_name(relationship_type_str)]
108        except KeyError:
109            raise SPDXParsingError([f"Invalid RelationshipType: {relationship_type_str}"])
110        return relationship_type
111
112    def parse_document_describes(
113        self, doc_spdx_id: str, described_spdx_ids: List[str], existing_relationships: List[Relationship]
114    ) -> List[Relationship]:
115        logger = Logger()
116        describes_relationships = []
117        for spdx_id in described_spdx_ids:
118            try:
119                describes_relationship = Relationship(
120                    spdx_element_id=doc_spdx_id,
121                    relationship_type=RelationshipType.DESCRIBES,
122                    related_spdx_element_id=spdx_id,
123                )
124            except ConstructorTypeErrors as err:
125                logger.append(err.get_messages())
126                continue
127            if not self.check_if_relationship_exists(describes_relationship, existing_relationships):
128                describes_relationships.append(describes_relationship)
129        raise_parsing_error_if_logger_has_messages(logger, "document describes relationships")
130
131        return describes_relationships
132
133    def parse_has_files(
134        self, package_dicts: List[Dict], existing_relationships: List[Relationship]
135    ) -> List[Relationship]:
136        # assume existing relationships are stripped of comments
137        logger = Logger()
138        contains_relationships = []
139        for package in package_dicts:
140            package_spdx_id: Optional[str] = package.get("SPDXID")
141            contained_files: List[str] = delete_duplicates_from_list(package.get("hasFiles", []))
142            if not contained_files:
143                continue
144            for file_spdx_id in contained_files:
145                try:
146                    contains_relationship = Relationship(
147                        spdx_element_id=package_spdx_id,
148                        relationship_type=RelationshipType.CONTAINS,
149                        related_spdx_element_id=file_spdx_id,
150                    )
151                except ConstructorTypeErrors as err:
152                    logger.append(err.get_messages())
153                    continue
154                if not self.check_if_relationship_exists(
155                    relationship=contains_relationship, existing_relationships=existing_relationships
156                ):
157                    contains_relationships.append(contains_relationship)
158        raise_parsing_error_if_logger_has_messages(logger, "package contains relationships")
159
160        return contains_relationships
161
162    def check_if_relationship_exists(
163        self, relationship: Relationship, existing_relationships: List[Relationship]
164    ) -> bool:
165        # assume existing relationships are stripped of comments
166        if relationship in existing_relationships:
167            return True
168        relationship_inverted: Relationship = self.invert_relationship(relationship)
169        if relationship_inverted in existing_relationships:
170            return True
171
172        return False
173
174    @staticmethod
175    def get_all_relationships_without_comments(existing_relationships: List[Relationship]) -> List[Relationship]:
176        relationships_without_comments = [
177            Relationship(
178                relationship_type=relationship.relationship_type,
179                related_spdx_element_id=relationship.related_spdx_element_id,
180                spdx_element_id=relationship.spdx_element_id,
181            )
182            for relationship in existing_relationships
183        ]
184        return relationships_without_comments
185
186    def invert_relationship(self, relationship: Relationship) -> Relationship:
187        return Relationship(
188            related_spdx_element_id=relationship.spdx_element_id,
189            spdx_element_id=relationship.related_spdx_element_id,
190            relationship_type=self.invert_relationship_types[relationship.relationship_type],
191            comment=relationship.comment,
192        )
193
194    invert_relationship_types = {
195        RelationshipType.DESCRIBES: RelationshipType.DESCRIBED_BY,
196        RelationshipType.DESCRIBED_BY: RelationshipType.DESCRIBES,
197        RelationshipType.CONTAINS: RelationshipType.CONTAINED_BY,
198        RelationshipType.CONTAINED_BY: RelationshipType.CONTAINS,
199    }
200
201    @staticmethod
202    def parse_file_dependencies(file_dicts: List[Dict]) -> List[Relationship]:
203        dependency_relationships = []
204        # the field fileDependencies is deprecated and should be converted to a relationship
205        # https://github.com/spdx/tools-python/issues/387
206        return dependency_relationships
207
208    @staticmethod
209    def parse_artifact_of(file_dicts: List[Dict]) -> List[Relationship]:
210        generated_relationships = []
211        # artifactOfs is deprecated and should be converted to an external package and a generated from relationship
212        # https://github.com/spdx/tools-python/issues/294
213        return generated_relationships
class RelationshipParser:
 23class RelationshipParser:
 24    logger: Logger
 25
 26    def __init__(self):
 27        self.logger = Logger()
 28
 29    def parse_all_relationships(self, input_doc_dict: Dict) -> List[Relationship]:
 30        relationships = []
 31        relationship_dicts: List[Dict] = input_doc_dict.get("relationships", [])
 32        relationships.extend(
 33            parse_field_or_log_error(self.logger, relationship_dicts, self.parse_relationship, [], True)
 34        )
 35
 36        document_describes: List[str] = delete_duplicates_from_list(input_doc_dict.get("documentDescribes", []))
 37        doc_spdx_id: Optional[str] = input_doc_dict.get("SPDXID")
 38
 39        existing_relationships_without_comments: List[Relationship] = self.get_all_relationships_without_comments(
 40            relationships
 41        )
 42        relationships.extend(
 43            parse_field_or_log_error(
 44                self.logger,
 45                document_describes,
 46                lambda x: self.parse_document_describes(
 47                    doc_spdx_id=doc_spdx_id,
 48                    described_spdx_ids=x,
 49                    existing_relationships=existing_relationships_without_comments,
 50                ),
 51                [],
 52            )
 53        )
 54
 55        package_dicts: List[Dict] = input_doc_dict.get("packages", [])
 56        existing_relationships_without_comments: List[Relationship] = self.get_all_relationships_without_comments(
 57            relationships
 58        )
 59
 60        relationships.extend(
 61            parse_field_or_log_error(
 62                self.logger,
 63                package_dicts,
 64                lambda x: self.parse_has_files(
 65                    package_dicts=x, existing_relationships=existing_relationships_without_comments
 66                ),
 67                [],
 68            )
 69        )
 70
 71        file_dicts: List[Dict] = input_doc_dict.get("files", [])
 72
 73        # not implemented yet: deal with deprecated fields in file:
 74        # https://github.com/spdx/tools-python/issues/294 & https://github.com/spdx/tools-python/issues/387
 75        _ = self.parse_artifact_of(file_dicts=file_dicts)
 76        _ = self.parse_file_dependencies(file_dicts=file_dicts)
 77
 78        raise_parsing_error_if_logger_has_messages(self.logger)
 79
 80        return relationships
 81
 82    def parse_relationship(self, relationship_dict: Dict) -> Relationship:
 83        logger = Logger()
 84        spdx_element_id: Optional[str] = relationship_dict.get("spdxElementId")
 85        related_spdx_element: Optional[str] = parse_field_or_no_assertion_or_none(
 86            relationship_dict.get("relatedSpdxElement")
 87        )
 88        relationship_type: Optional[RelationshipType] = parse_field_or_log_error(
 89            logger, relationship_dict.get("relationshipType"), self.parse_relationship_type
 90        )
 91        relationship_comment: Optional[str] = relationship_dict.get("comment")
 92        raise_parsing_error_if_logger_has_messages(logger, "Relationship")
 93
 94        relationship = construct_or_raise_parsing_error(
 95            Relationship,
 96            dict(
 97                spdx_element_id=spdx_element_id,
 98                relationship_type=relationship_type,
 99                related_spdx_element_id=related_spdx_element,
100                comment=relationship_comment,
101            ),
102        )
103        return relationship
104
105    @staticmethod
106    def parse_relationship_type(relationship_type_str: str) -> RelationshipType:
107        try:
108            relationship_type = RelationshipType[json_str_to_enum_name(relationship_type_str)]
109        except KeyError:
110            raise SPDXParsingError([f"Invalid RelationshipType: {relationship_type_str}"])
111        return relationship_type
112
113    def parse_document_describes(
114        self, doc_spdx_id: str, described_spdx_ids: List[str], existing_relationships: List[Relationship]
115    ) -> List[Relationship]:
116        logger = Logger()
117        describes_relationships = []
118        for spdx_id in described_spdx_ids:
119            try:
120                describes_relationship = Relationship(
121                    spdx_element_id=doc_spdx_id,
122                    relationship_type=RelationshipType.DESCRIBES,
123                    related_spdx_element_id=spdx_id,
124                )
125            except ConstructorTypeErrors as err:
126                logger.append(err.get_messages())
127                continue
128            if not self.check_if_relationship_exists(describes_relationship, existing_relationships):
129                describes_relationships.append(describes_relationship)
130        raise_parsing_error_if_logger_has_messages(logger, "document describes relationships")
131
132        return describes_relationships
133
134    def parse_has_files(
135        self, package_dicts: List[Dict], existing_relationships: List[Relationship]
136    ) -> List[Relationship]:
137        # assume existing relationships are stripped of comments
138        logger = Logger()
139        contains_relationships = []
140        for package in package_dicts:
141            package_spdx_id: Optional[str] = package.get("SPDXID")
142            contained_files: List[str] = delete_duplicates_from_list(package.get("hasFiles", []))
143            if not contained_files:
144                continue
145            for file_spdx_id in contained_files:
146                try:
147                    contains_relationship = Relationship(
148                        spdx_element_id=package_spdx_id,
149                        relationship_type=RelationshipType.CONTAINS,
150                        related_spdx_element_id=file_spdx_id,
151                    )
152                except ConstructorTypeErrors as err:
153                    logger.append(err.get_messages())
154                    continue
155                if not self.check_if_relationship_exists(
156                    relationship=contains_relationship, existing_relationships=existing_relationships
157                ):
158                    contains_relationships.append(contains_relationship)
159        raise_parsing_error_if_logger_has_messages(logger, "package contains relationships")
160
161        return contains_relationships
162
163    def check_if_relationship_exists(
164        self, relationship: Relationship, existing_relationships: List[Relationship]
165    ) -> bool:
166        # assume existing relationships are stripped of comments
167        if relationship in existing_relationships:
168            return True
169        relationship_inverted: Relationship = self.invert_relationship(relationship)
170        if relationship_inverted in existing_relationships:
171            return True
172
173        return False
174
175    @staticmethod
176    def get_all_relationships_without_comments(existing_relationships: List[Relationship]) -> List[Relationship]:
177        relationships_without_comments = [
178            Relationship(
179                relationship_type=relationship.relationship_type,
180                related_spdx_element_id=relationship.related_spdx_element_id,
181                spdx_element_id=relationship.spdx_element_id,
182            )
183            for relationship in existing_relationships
184        ]
185        return relationships_without_comments
186
187    def invert_relationship(self, relationship: Relationship) -> Relationship:
188        return Relationship(
189            related_spdx_element_id=relationship.spdx_element_id,
190            spdx_element_id=relationship.related_spdx_element_id,
191            relationship_type=self.invert_relationship_types[relationship.relationship_type],
192            comment=relationship.comment,
193        )
194
195    invert_relationship_types = {
196        RelationshipType.DESCRIBES: RelationshipType.DESCRIBED_BY,
197        RelationshipType.DESCRIBED_BY: RelationshipType.DESCRIBES,
198        RelationshipType.CONTAINS: RelationshipType.CONTAINED_BY,
199        RelationshipType.CONTAINED_BY: RelationshipType.CONTAINS,
200    }
201
202    @staticmethod
203    def parse_file_dependencies(file_dicts: List[Dict]) -> List[Relationship]:
204        dependency_relationships = []
205        # the field fileDependencies is deprecated and should be converted to a relationship
206        # https://github.com/spdx/tools-python/issues/387
207        return dependency_relationships
208
209    @staticmethod
210    def parse_artifact_of(file_dicts: List[Dict]) -> List[Relationship]:
211        generated_relationships = []
212        # artifactOfs is deprecated and should be converted to an external package and a generated from relationship
213        # https://github.com/spdx/tools-python/issues/294
214        return generated_relationships
def parse_all_relationships( self, input_doc_dict: dict) -> list[spdx_tools.spdx.model.relationship.Relationship]:
29    def parse_all_relationships(self, input_doc_dict: Dict) -> List[Relationship]:
30        relationships = []
31        relationship_dicts: List[Dict] = input_doc_dict.get("relationships", [])
32        relationships.extend(
33            parse_field_or_log_error(self.logger, relationship_dicts, self.parse_relationship, [], True)
34        )
35
36        document_describes: List[str] = delete_duplicates_from_list(input_doc_dict.get("documentDescribes", []))
37        doc_spdx_id: Optional[str] = input_doc_dict.get("SPDXID")
38
39        existing_relationships_without_comments: List[Relationship] = self.get_all_relationships_without_comments(
40            relationships
41        )
42        relationships.extend(
43            parse_field_or_log_error(
44                self.logger,
45                document_describes,
46                lambda x: self.parse_document_describes(
47                    doc_spdx_id=doc_spdx_id,
48                    described_spdx_ids=x,
49                    existing_relationships=existing_relationships_without_comments,
50                ),
51                [],
52            )
53        )
54
55        package_dicts: List[Dict] = input_doc_dict.get("packages", [])
56        existing_relationships_without_comments: List[Relationship] = self.get_all_relationships_without_comments(
57            relationships
58        )
59
60        relationships.extend(
61            parse_field_or_log_error(
62                self.logger,
63                package_dicts,
64                lambda x: self.parse_has_files(
65                    package_dicts=x, existing_relationships=existing_relationships_without_comments
66                ),
67                [],
68            )
69        )
70
71        file_dicts: List[Dict] = input_doc_dict.get("files", [])
72
73        # not implemented yet: deal with deprecated fields in file:
74        # https://github.com/spdx/tools-python/issues/294 & https://github.com/spdx/tools-python/issues/387
75        _ = self.parse_artifact_of(file_dicts=file_dicts)
76        _ = self.parse_file_dependencies(file_dicts=file_dicts)
77
78        raise_parsing_error_if_logger_has_messages(self.logger)
79
80        return relationships
def parse_relationship( self, relationship_dict: dict) -> spdx_tools.spdx.model.relationship.Relationship:
 82    def parse_relationship(self, relationship_dict: Dict) -> Relationship:
 83        logger = Logger()
 84        spdx_element_id: Optional[str] = relationship_dict.get("spdxElementId")
 85        related_spdx_element: Optional[str] = parse_field_or_no_assertion_or_none(
 86            relationship_dict.get("relatedSpdxElement")
 87        )
 88        relationship_type: Optional[RelationshipType] = parse_field_or_log_error(
 89            logger, relationship_dict.get("relationshipType"), self.parse_relationship_type
 90        )
 91        relationship_comment: Optional[str] = relationship_dict.get("comment")
 92        raise_parsing_error_if_logger_has_messages(logger, "Relationship")
 93
 94        relationship = construct_or_raise_parsing_error(
 95            Relationship,
 96            dict(
 97                spdx_element_id=spdx_element_id,
 98                relationship_type=relationship_type,
 99                related_spdx_element_id=related_spdx_element,
100                comment=relationship_comment,
101            ),
102        )
103        return relationship
@staticmethod
def parse_relationship_type( relationship_type_str: str) -> spdx_tools.spdx.model.relationship.RelationshipType:
105    @staticmethod
106    def parse_relationship_type(relationship_type_str: str) -> RelationshipType:
107        try:
108            relationship_type = RelationshipType[json_str_to_enum_name(relationship_type_str)]
109        except KeyError:
110            raise SPDXParsingError([f"Invalid RelationshipType: {relationship_type_str}"])
111        return relationship_type
def parse_document_describes( self, doc_spdx_id: str, described_spdx_ids: list[str], existing_relationships: list[spdx_tools.spdx.model.relationship.Relationship]) -> list[spdx_tools.spdx.model.relationship.Relationship]:
113    def parse_document_describes(
114        self, doc_spdx_id: str, described_spdx_ids: List[str], existing_relationships: List[Relationship]
115    ) -> List[Relationship]:
116        logger = Logger()
117        describes_relationships = []
118        for spdx_id in described_spdx_ids:
119            try:
120                describes_relationship = Relationship(
121                    spdx_element_id=doc_spdx_id,
122                    relationship_type=RelationshipType.DESCRIBES,
123                    related_spdx_element_id=spdx_id,
124                )
125            except ConstructorTypeErrors as err:
126                logger.append(err.get_messages())
127                continue
128            if not self.check_if_relationship_exists(describes_relationship, existing_relationships):
129                describes_relationships.append(describes_relationship)
130        raise_parsing_error_if_logger_has_messages(logger, "document describes relationships")
131
132        return describes_relationships
def parse_has_files( self, package_dicts: list[dict], existing_relationships: list[spdx_tools.spdx.model.relationship.Relationship]) -> list[spdx_tools.spdx.model.relationship.Relationship]:
134    def parse_has_files(
135        self, package_dicts: List[Dict], existing_relationships: List[Relationship]
136    ) -> List[Relationship]:
137        # assume existing relationships are stripped of comments
138        logger = Logger()
139        contains_relationships = []
140        for package in package_dicts:
141            package_spdx_id: Optional[str] = package.get("SPDXID")
142            contained_files: List[str] = delete_duplicates_from_list(package.get("hasFiles", []))
143            if not contained_files:
144                continue
145            for file_spdx_id in contained_files:
146                try:
147                    contains_relationship = Relationship(
148                        spdx_element_id=package_spdx_id,
149                        relationship_type=RelationshipType.CONTAINS,
150                        related_spdx_element_id=file_spdx_id,
151                    )
152                except ConstructorTypeErrors as err:
153                    logger.append(err.get_messages())
154                    continue
155                if not self.check_if_relationship_exists(
156                    relationship=contains_relationship, existing_relationships=existing_relationships
157                ):
158                    contains_relationships.append(contains_relationship)
159        raise_parsing_error_if_logger_has_messages(logger, "package contains relationships")
160
161        return contains_relationships
def check_if_relationship_exists( self, relationship: spdx_tools.spdx.model.relationship.Relationship, existing_relationships: list[spdx_tools.spdx.model.relationship.Relationship]) -> bool:
163    def check_if_relationship_exists(
164        self, relationship: Relationship, existing_relationships: List[Relationship]
165    ) -> bool:
166        # assume existing relationships are stripped of comments
167        if relationship in existing_relationships:
168            return True
169        relationship_inverted: Relationship = self.invert_relationship(relationship)
170        if relationship_inverted in existing_relationships:
171            return True
172
173        return False
@staticmethod
def get_all_relationships_without_comments( existing_relationships: list[spdx_tools.spdx.model.relationship.Relationship]) -> list[spdx_tools.spdx.model.relationship.Relationship]:
175    @staticmethod
176    def get_all_relationships_without_comments(existing_relationships: List[Relationship]) -> List[Relationship]:
177        relationships_without_comments = [
178            Relationship(
179                relationship_type=relationship.relationship_type,
180                related_spdx_element_id=relationship.related_spdx_element_id,
181                spdx_element_id=relationship.spdx_element_id,
182            )
183            for relationship in existing_relationships
184        ]
185        return relationships_without_comments
def invert_relationship( self, relationship: spdx_tools.spdx.model.relationship.Relationship) -> spdx_tools.spdx.model.relationship.Relationship:
187    def invert_relationship(self, relationship: Relationship) -> Relationship:
188        return Relationship(
189            related_spdx_element_id=relationship.spdx_element_id,
190            spdx_element_id=relationship.related_spdx_element_id,
191            relationship_type=self.invert_relationship_types[relationship.relationship_type],
192            comment=relationship.comment,
193        )
invert_relationship_types = {<RelationshipType.DESCRIBES: 14>: <RelationshipType.DESCRIBED_BY: 13>, <RelationshipType.DESCRIBED_BY: 13>: <RelationshipType.DESCRIBES: 14>, <RelationshipType.CONTAINS: 6>: <RelationshipType.CONTAINED_BY: 5>, <RelationshipType.CONTAINED_BY: 5>: <RelationshipType.CONTAINS: 6>}
@staticmethod
def parse_file_dependencies( file_dicts: list[dict]) -> list[spdx_tools.spdx.model.relationship.Relationship]:
202    @staticmethod
203    def parse_file_dependencies(file_dicts: List[Dict]) -> List[Relationship]:
204        dependency_relationships = []
205        # the field fileDependencies is deprecated and should be converted to a relationship
206        # https://github.com/spdx/tools-python/issues/387
207        return dependency_relationships
@staticmethod
def parse_artifact_of( file_dicts: list[dict]) -> list[spdx_tools.spdx.model.relationship.Relationship]:
209    @staticmethod
210    def parse_artifact_of(file_dicts: List[Dict]) -> List[Relationship]:
211        generated_relationships = []
212        # artifactOfs is deprecated and should be converted to an external package and a generated from relationship
213        # https://github.com/spdx/tools-python/issues/294
214        return generated_relationships