spdx_tools.spdx3.bump_from_spdx2.relationship

  1# SPDX-FileCopyrightText: 2023 spdx contributors
  2#
  3# SPDX-License-Identifier: Apache-2.0
  4import logging
  5import sys
  6
  7from beartype.typing import Dict, List, Optional, Tuple, Union
  8
  9from spdx_tools.spdx3.bump_from_spdx2.message import print_missing_conversion
 10from spdx_tools.spdx3.model import LifecycleScopeType, Relationship, RelationshipCompleteness, RelationshipType
 11from spdx_tools.spdx3.model.software import (
 12    DependencyConditionalityType,
 13    SoftwareDependencyLinkType,
 14    SoftwareDependencyRelationship,
 15)
 16from spdx_tools.spdx3.payload import Payload
 17from spdx_tools.spdx.model.relationship import Relationship as Spdx2_Relationship
 18from spdx_tools.spdx.model.relationship import RelationshipType as Spdx2_RelationshipType
 19from spdx_tools.spdx.model.spdx_no_assertion import SpdxNoAssertion
 20from spdx_tools.spdx.model.spdx_none import SpdxNone
 21
 22# bump relationship type, map each relationship type to the corresponding class in 3.0,
 23# the relationship type, other arguments and if swapped
 24relationship_mapping: Dict[
 25    Spdx2_RelationshipType,
 26    Tuple[
 27        Union[Relationship, SoftwareDependencyRelationship],
 28        RelationshipType,
 29        Dict[str, Union[bool, LifecycleScopeType, SoftwareDependencyLinkType, DependencyConditionalityType]],
 30    ],
 31] = {
 32    Spdx2_RelationshipType.AMENDS: (Relationship, RelationshipType.AMENDS, {}),
 33    Spdx2_RelationshipType.ANCESTOR_OF: (Relationship, RelationshipType.ANCESTOR, {}),
 34    Spdx2_RelationshipType.BUILD_DEPENDENCY_OF: (
 35        SoftwareDependencyRelationship,
 36        RelationshipType.DEPENDS_ON,
 37        {
 38            "scope": LifecycleScopeType.BUILD,
 39            "linkage": SoftwareDependencyLinkType.TOOL,
 40        },
 41    ),
 42    Spdx2_RelationshipType.BUILD_TOOL_OF: (
 43        SoftwareDependencyRelationship,
 44        RelationshipType.DEPENDS_ON,
 45        {"scope": LifecycleScopeType.BUILD, "linkage": SoftwareDependencyLinkType.TOOL},
 46    ),
 47    Spdx2_RelationshipType.CONTAINED_BY: (Relationship, RelationshipType.CONTAINS, {"swap": True}),
 48    Spdx2_RelationshipType.CONTAINS: (
 49        Relationship,
 50        RelationshipType.CONTAINS,
 51        {},
 52    ),  # might be deleted in favor of depends on
 53    Spdx2_RelationshipType.COPY_OF: (Relationship, RelationshipType.COPY, {}),
 54    Spdx2_RelationshipType.DATA_FILE_OF: (None, None, {}),  # not defined, probably input/ output
 55    Spdx2_RelationshipType.DEPENDENCY_MANIFEST_OF: (
 56        SoftwareDependencyRelationship,
 57        RelationshipType.DEPENDS_ON,
 58        {},
 59    ),  # "expect purpose has been set to manifest"
 60    Spdx2_RelationshipType.DEPENDENCY_OF: (
 61        SoftwareDependencyRelationship,
 62        RelationshipType.DEPENDS_ON,
 63        {"swap": True},
 64    ),
 65    Spdx2_RelationshipType.DEPENDS_ON: (SoftwareDependencyRelationship, RelationshipType.DEPENDS_ON, {}),
 66    Spdx2_RelationshipType.DESCENDANT_OF: (Relationship, RelationshipType.ANCESTOR, {"swap": True}),
 67    Spdx2_RelationshipType.DESCRIBED_BY: (Relationship, RelationshipType.DESCRIBES, {"swap": True}),
 68    Spdx2_RelationshipType.DESCRIBES: (
 69        Relationship,
 70        RelationshipType.DESCRIBES,
 71        {},
 72    ),  # might be deleted in favor of root
 73    # property
 74    Spdx2_RelationshipType.DEV_DEPENDENCY_OF: (
 75        SoftwareDependencyRelationship,
 76        RelationshipType.DEPENDS_ON,
 77        {"scope": LifecycleScopeType.DEVELOPMENT},
 78    ),
 79    Spdx2_RelationshipType.DEV_TOOL_OF: (
 80        SoftwareDependencyRelationship,
 81        RelationshipType.DEPENDS_ON,
 82        {"scope": LifecycleScopeType.DEVELOPMENT, "linkage": SoftwareDependencyLinkType.TOOL},
 83    ),
 84    Spdx2_RelationshipType.DISTRIBUTION_ARTIFACT: (None, None, {}),  # not defined yet, purpose?
 85    Spdx2_RelationshipType.DOCUMENTATION_OF: (Relationship, RelationshipType.DOCUMENTATION, {}),
 86    Spdx2_RelationshipType.DYNAMIC_LINK: (
 87        SoftwareDependencyRelationship,
 88        RelationshipType.DEPENDS_ON,
 89        {"linkage": SoftwareDependencyLinkType.DYNAMIC},
 90    ),
 91    Spdx2_RelationshipType.EXAMPLE_OF: (Relationship, RelationshipType.EXAMPLE, {}),
 92    Spdx2_RelationshipType.EXPANDED_FROM_ARCHIVE: (Relationship, RelationshipType.EXPANDED_FROM_ARCHIVE, {}),
 93    Spdx2_RelationshipType.FILE_ADDED: (Relationship, RelationshipType.FILE_ADDED, {}),
 94    Spdx2_RelationshipType.FILE_DELETED: (Relationship, RelationshipType.FILE_DELETED, {}),
 95    Spdx2_RelationshipType.FILE_MODIFIED: (Relationship, RelationshipType.FILE_MODIFIED, {}),
 96    Spdx2_RelationshipType.GENERATED_FROM: (Relationship, RelationshipType.GENERATES, {"swap": True}),
 97    Spdx2_RelationshipType.GENERATES: (Relationship, RelationshipType.GENERATES, {}),
 98    Spdx2_RelationshipType.HAS_PREREQUISITE: (
 99        SoftwareDependencyRelationship,
100        RelationshipType.DEPENDS_ON,
101        {"conditionality": DependencyConditionalityType.PREREQUISITE},
102    ),
103    Spdx2_RelationshipType.METAFILE_OF: (Relationship, RelationshipType.METAFILE, {}),
104    Spdx2_RelationshipType.OPTIONAL_COMPONENT_OF: (None, None, {}),  # converted to depends on and purpose? not clear
105    Spdx2_RelationshipType.OPTIONAL_DEPENDENCY_OF: (
106        SoftwareDependencyRelationship,
107        RelationshipType.DEPENDS_ON,
108        {"conditionality": DependencyConditionalityType.OPTIONAL},
109    ),
110    Spdx2_RelationshipType.OTHER: (Relationship, RelationshipType.OTHER, {}),
111    Spdx2_RelationshipType.PACKAGE_OF: (SoftwareDependencyRelationship, RelationshipType.DEPENDS_ON, {}),
112    Spdx2_RelationshipType.PATCH_APPLIED: (Relationship, RelationshipType.PATCH, {"swap": True}),
113    Spdx2_RelationshipType.PATCH_FOR: (Relationship, RelationshipType.PATCH, {}),
114    Spdx2_RelationshipType.PREREQUISITE_FOR: (
115        SoftwareDependencyRelationship,
116        RelationshipType.DEPENDS_ON,
117        {"conditionality": DependencyConditionalityType.PREREQUISITE},
118    ),
119    Spdx2_RelationshipType.PROVIDED_DEPENDENCY_OF: (
120        SoftwareDependencyRelationship,
121        RelationshipType.DEPENDS_ON,
122        {"scope": LifecycleScopeType.BUILD, "conditionality": DependencyConditionalityType.PROVIDED},
123    ),
124    Spdx2_RelationshipType.RUNTIME_DEPENDENCY_OF: (
125        SoftwareDependencyRelationship,
126        RelationshipType.DEPENDS_ON,
127        {"scope": LifecycleScopeType.RUNTIME},
128    ),
129    Spdx2_RelationshipType.STATIC_LINK: (
130        SoftwareDependencyRelationship,
131        RelationshipType.DEPENDS_ON,
132        {"linkage": SoftwareDependencyLinkType.STATIC},
133    ),
134    Spdx2_RelationshipType.TEST_CASE_OF: (Relationship, RelationshipType.TEST_CASE, {}),
135    Spdx2_RelationshipType.TEST_DEPENDENCY_OF: (
136        SoftwareDependencyRelationship,
137        RelationshipType.DEPENDS_ON,
138        {"scope": LifecycleScopeType.TEST},
139    ),
140    Spdx2_RelationshipType.TEST_OF: (Relationship, RelationshipType.TEST, {}),
141    Spdx2_RelationshipType.TEST_TOOL_OF: (
142        SoftwareDependencyRelationship,
143        RelationshipType.DEPENDS_ON,
144        {"scope": LifecycleScopeType.TEST, "linkage": SoftwareDependencyLinkType.TOOL},
145    ),
146    Spdx2_RelationshipType.VARIANT_OF: (Relationship, RelationshipType.VARIANT, {}),
147    Spdx2_RelationshipType.REQUIREMENT_DESCRIPTION_FOR: (Relationship, RelationshipType.REQUIREMENT_FOR, {}),
148    Spdx2_RelationshipType.SPECIFICATION_FOR: (Relationship, RelationshipType.SPECIFICATION_FOR, {}),
149}
150
151
152def bump_relationships(
153    spdx2_relationships: List[Spdx2_Relationship],
154    payload: Payload,
155    document_namespace: str,
156):
157    generated_relationships: Dict[Tuple[str, str], List[Relationship]] = {}
158    for counter, spdx2_relationship in enumerate(spdx2_relationships):
159        relationship = bump_relationship(spdx2_relationship, document_namespace, counter)
160        if relationship:
161            generated_relationships.setdefault(
162                (relationship.from_element, relationship.relationship_type.name), []
163            ).append(relationship)
164
165    for relationships in generated_relationships.values():
166        if len(relationships) > 1:
167            _merge_relationships_and_add_to_payload(relationships, payload)
168        else:
169            payload.add_element(relationships[0])
170
171
172def bump_relationship(
173    spdx2_relationship: Spdx2_Relationship,
174    document_namespace: str,
175    counter: int,
176) -> Optional[Union[Relationship, SoftwareDependencyRelationship]]:
177    completeness, to = determine_completeness_and_to(spdx2_relationship.related_spdx_element_id)
178    spdx_id = "#".join([document_namespace, f"SPDXRef-Relationship-{counter}"])
179    relationship_class, relationship_type, parameters = relationship_mapping[spdx2_relationship.relationship_type]
180    if relationship_class is None:
181        print_missing_conversion(spdx2_relationship.relationship_type.name, 0)
182        return
183
184    swap_direction = parameters.get("swap", False)
185
186    if swap_direction:
187        if not to:
188            print_missing_conversion("Swapped Relationship to NoAssertion/None", 0)
189            return
190        from_element = to[0]
191        to = [spdx2_relationship.spdx_element_id]
192    else:
193        from_element = spdx2_relationship.spdx_element_id
194
195    if relationship_class == SoftwareDependencyRelationship:
196        from_element = spdx2_relationship.spdx_element_id
197
198        return SoftwareDependencyRelationship(
199            spdx_id,
200            f"{document_namespace}#{from_element}",
201            relationship_type,
202            [f"{document_namespace}#{t}" for t in to],
203            comment=spdx2_relationship.comment,
204            completeness=completeness,
205            scope=parameters.get("scope"),
206            software_linkage=parameters.get("linkage"),
207            conditionality=parameters.get("conditionality"),
208        )
209
210    return Relationship(
211        spdx_id,
212        f"{document_namespace}#{from_element}",
213        relationship_type,
214        [f"{document_namespace}#{t}" for t in to],
215        comment=spdx2_relationship.comment,
216        completeness=completeness,
217    )
218
219
220def determine_completeness_and_to(
221    related_spdx_element_id: Union[str, SpdxNone, SpdxNoAssertion]
222) -> Tuple[Optional[RelationshipCompleteness], List[str]]:
223    if isinstance(related_spdx_element_id, SpdxNoAssertion):
224        completeness = RelationshipCompleteness.NOASSERTION
225        to = []
226    elif isinstance(related_spdx_element_id, SpdxNone):
227        completeness = RelationshipCompleteness.COMPLETE
228        to = []
229    else:
230        completeness = None
231        to = [related_spdx_element_id]
232    return completeness, to
233
234
235def _merge_relationships_and_add_to_payload(relationships: List[Relationship], payload: Payload):
236    to = []
237    completeness = None
238    spdx_id = None
239    merged_relationship = relationships[0]
240    for merged_relationship in relationships:
241        if merged_relationship.comment:
242            payload.add_element(merged_relationship)
243            continue
244        if merged_relationship.completeness:
245            if completeness and completeness != merged_relationship.completeness:
246                logging.warning(
247                    f"Contradicting information about completeness of relationship: {merged_relationship}", sys.stderr
248                )
249            else:
250                completeness = merged_relationship.completeness
251
252        to += merged_relationship.to
253        spdx_id = merged_relationship.spdx_id
254    if to:
255        merged_relationship.spdx_id = spdx_id
256        merged_relationship.to = to
257        merged_relationship.completeness = completeness
258        merged_relationship.comment = None
259        payload.add_element(merged_relationship)
relationship_mapping: dict[spdx_tools.spdx.model.relationship.RelationshipType, tuple[typing.Union[spdx_tools.spdx3.model.relationship.Relationship, spdx_tools.spdx3.model.software.software_dependency_relationship.SoftwareDependencyRelationship], spdx_tools.spdx3.model.relationship.RelationshipType, dict[str, typing.Union[bool, spdx_tools.spdx3.model.lifecycle_scoped_relationship.LifecycleScopeType, spdx_tools.spdx3.model.software.software_dependency_relationship.SoftwareDependencyLinkType, spdx_tools.spdx3.model.software.software_dependency_relationship.DependencyConditionalityType]]]] = {<RelationshipType.AMENDS: 1>: (<class 'spdx_tools.spdx3.model.relationship.Relationship'>, <RelationshipType.AMENDS: 2>, {}), <RelationshipType.ANCESTOR_OF: 2>: (<class 'spdx_tools.spdx3.model.relationship.Relationship'>, <RelationshipType.ANCESTOR: 3>, {}), <RelationshipType.BUILD_DEPENDENCY_OF: 3>: (<class 'spdx_tools.spdx3.model.software.software_dependency_relationship.SoftwareDependencyRelationship'>, <RelationshipType.DEPENDS_ON: 13>, {'scope': <LifecycleScopeType.BUILD: 2>, 'linkage': <SoftwareDependencyLinkType.TOOL: 3>}), <RelationshipType.BUILD_TOOL_OF: 4>: (<class 'spdx_tools.spdx3.model.software.software_dependency_relationship.SoftwareDependencyRelationship'>, <RelationshipType.DEPENDS_ON: 13>, {'scope': <LifecycleScopeType.BUILD: 2>, 'linkage': <SoftwareDependencyLinkType.TOOL: 3>}), <RelationshipType.CONTAINED_BY: 5>: (<class 'spdx_tools.spdx3.model.relationship.Relationship'>, <RelationshipType.CONTAINS: 8>, {'swap': True}), <RelationshipType.CONTAINS: 6>: (<class 'spdx_tools.spdx3.model.relationship.Relationship'>, <RelationshipType.CONTAINS: 8>, {}), <RelationshipType.COPY_OF: 7>: (<class 'spdx_tools.spdx3.model.relationship.Relationship'>, <RelationshipType.COPY: 10>, {}), <RelationshipType.DATA_FILE_OF: 8>: (None, None, {}), <RelationshipType.DEPENDENCY_MANIFEST_OF: 9>: (<class 'spdx_tools.spdx3.model.software.software_dependency_relationship.SoftwareDependencyRelationship'>, <RelationshipType.DEPENDS_ON: 13>, {}), <RelationshipType.DEPENDENCY_OF: 10>: (<class 'spdx_tools.spdx3.model.software.software_dependency_relationship.SoftwareDependencyRelationship'>, <RelationshipType.DEPENDS_ON: 13>, {'swap': True}), <RelationshipType.DEPENDS_ON: 11>: (<class 'spdx_tools.spdx3.model.software.software_dependency_relationship.SoftwareDependencyRelationship'>, <RelationshipType.DEPENDS_ON: 13>, {}), <RelationshipType.DESCENDANT_OF: 12>: (<class 'spdx_tools.spdx3.model.relationship.Relationship'>, <RelationshipType.ANCESTOR: 3>, {'swap': True}), <RelationshipType.DESCRIBED_BY: 13>: (<class 'spdx_tools.spdx3.model.relationship.Relationship'>, <RelationshipType.DESCRIBES: 15>, {'swap': True}), <RelationshipType.DESCRIBES: 14>: (<class 'spdx_tools.spdx3.model.relationship.Relationship'>, <RelationshipType.DESCRIBES: 15>, {}), <RelationshipType.DEV_DEPENDENCY_OF: 15>: (<class 'spdx_tools.spdx3.model.software.software_dependency_relationship.SoftwareDependencyRelationship'>, <RelationshipType.DEPENDS_ON: 13>, {'scope': <LifecycleScopeType.DEVELOPMENT: 3>}), <RelationshipType.DEV_TOOL_OF: 16>: (<class 'spdx_tools.spdx3.model.software.software_dependency_relationship.SoftwareDependencyRelationship'>, <RelationshipType.DEPENDS_ON: 13>, {'scope': <LifecycleScopeType.DEVELOPMENT: 3>, 'linkage': <SoftwareDependencyLinkType.TOOL: 3>}), <RelationshipType.DISTRIBUTION_ARTIFACT: 17>: (None, None, {}), <RelationshipType.DOCUMENTATION_OF: 18>: (<class 'spdx_tools.spdx3.model.relationship.Relationship'>, <RelationshipType.DOCUMENTATION: 19>, {}), <RelationshipType.DYNAMIC_LINK: 19>: (<class 'spdx_tools.spdx3.model.software.software_dependency_relationship.SoftwareDependencyRelationship'>, <RelationshipType.DEPENDS_ON: 13>, {'linkage': <SoftwareDependencyLinkType.DYNAMIC: 2>}), <RelationshipType.EXAMPLE_OF: 20>: (<class 'spdx_tools.spdx3.model.relationship.Relationship'>, <RelationshipType.EXAMPLE: 22>, {}), <RelationshipType.EXPANDED_FROM_ARCHIVE: 21>: (<class 'spdx_tools.spdx3.model.relationship.Relationship'>, <RelationshipType.EXPANDED_FROM_ARCHIVE: 24>, {}), <RelationshipType.FILE_ADDED: 22>: (<class 'spdx_tools.spdx3.model.relationship.Relationship'>, <RelationshipType.FILE_ADDED: 26>, {}), <RelationshipType.FILE_DELETED: 23>: (<class 'spdx_tools.spdx3.model.relationship.Relationship'>, <RelationshipType.FILE_DELETED: 27>, {}), <RelationshipType.FILE_MODIFIED: 24>: (<class 'spdx_tools.spdx3.model.relationship.Relationship'>, <RelationshipType.FILE_MODIFIED: 28>, {}), <RelationshipType.GENERATED_FROM: 25>: (<class 'spdx_tools.spdx3.model.relationship.Relationship'>, <RelationshipType.GENERATES: 32>, {'swap': True}), <RelationshipType.GENERATES: 26>: (<class 'spdx_tools.spdx3.model.relationship.Relationship'>, <RelationshipType.GENERATES: 32>, {}), <RelationshipType.HAS_PREREQUISITE: 27>: (<class 'spdx_tools.spdx3.model.software.software_dependency_relationship.SoftwareDependencyRelationship'>, <RelationshipType.DEPENDS_ON: 13>, {'conditionality': <DependencyConditionalityType.PREREQUISITE: 4>}), <RelationshipType.METAFILE_OF: 28>: (<class 'spdx_tools.spdx3.model.relationship.Relationship'>, <RelationshipType.METAFILE: 38>, {}), <RelationshipType.OPTIONAL_COMPONENT_OF: 29>: (None, None, {}), <RelationshipType.OPTIONAL_DEPENDENCY_OF: 30>: (<class 'spdx_tools.spdx3.model.software.software_dependency_relationship.SoftwareDependencyRelationship'>, <RelationshipType.DEPENDS_ON: 13>, {'conditionality': <DependencyConditionalityType.OPTIONAL: 1>}), <RelationshipType.OTHER: 31>: (<class 'spdx_tools.spdx3.model.relationship.Relationship'>, <RelationshipType.OTHER: 42>, {}), <RelationshipType.PACKAGE_OF: 32>: (<class 'spdx_tools.spdx3.model.software.software_dependency_relationship.SoftwareDependencyRelationship'>, <RelationshipType.DEPENDS_ON: 13>, {}), <RelationshipType.PATCH_APPLIED: 33>: (<class 'spdx_tools.spdx3.model.relationship.Relationship'>, <RelationshipType.PATCH: 45>, {'swap': True}), <RelationshipType.PATCH_FOR: 34>: (<class 'spdx_tools.spdx3.model.relationship.Relationship'>, <RelationshipType.PATCH: 45>, {}), <RelationshipType.PREREQUISITE_FOR: 35>: (<class 'spdx_tools.spdx3.model.software.software_dependency_relationship.SoftwareDependencyRelationship'>, <RelationshipType.DEPENDS_ON: 13>, {'conditionality': <DependencyConditionalityType.PREREQUISITE: 4>}), <RelationshipType.PROVIDED_DEPENDENCY_OF: 36>: (<class 'spdx_tools.spdx3.model.software.software_dependency_relationship.SoftwareDependencyRelationship'>, <RelationshipType.DEPENDS_ON: 13>, {'scope': <LifecycleScopeType.BUILD: 2>, 'conditionality': <DependencyConditionalityType.PROVIDED: 3>}), <RelationshipType.RUNTIME_DEPENDENCY_OF: 38>: (<class 'spdx_tools.spdx3.model.software.software_dependency_relationship.SoftwareDependencyRelationship'>, <RelationshipType.DEPENDS_ON: 13>, {'scope': <LifecycleScopeType.RUNTIME: 5>}), <RelationshipType.STATIC_LINK: 40>: (<class 'spdx_tools.spdx3.model.software.software_dependency_relationship.SoftwareDependencyRelationship'>, <RelationshipType.DEPENDS_ON: 13>, {'linkage': <SoftwareDependencyLinkType.STATIC: 1>}), <RelationshipType.TEST_CASE_OF: 41>: (<class 'spdx_tools.spdx3.model.relationship.Relationship'>, <RelationshipType.TEST_CASE: 56>, {}), <RelationshipType.TEST_DEPENDENCY_OF: 42>: (<class 'spdx_tools.spdx3.model.software.software_dependency_relationship.SoftwareDependencyRelationship'>, <RelationshipType.DEPENDS_ON: 13>, {'scope': <LifecycleScopeType.TEST: 4>}), <RelationshipType.TEST_OF: 43>: (<class 'spdx_tools.spdx3.model.relationship.Relationship'>, <RelationshipType.TEST: 55>, {}), <RelationshipType.TEST_TOOL_OF: 44>: (<class 'spdx_tools.spdx3.model.software.software_dependency_relationship.SoftwareDependencyRelationship'>, <RelationshipType.DEPENDS_ON: 13>, {'scope': <LifecycleScopeType.TEST: 4>, 'linkage': <SoftwareDependencyLinkType.TOOL: 3>}), <RelationshipType.VARIANT_OF: 45>: (<class 'spdx_tools.spdx3.model.relationship.Relationship'>, <RelationshipType.VARIANT: 62>, {}), <RelationshipType.REQUIREMENT_DESCRIPTION_FOR: 37>: (<class 'spdx_tools.spdx3.model.relationship.Relationship'>, <RelationshipType.REQUIREMENT_FOR: 51>, {}), <RelationshipType.SPECIFICATION_FOR: 39>: (<class 'spdx_tools.spdx3.model.relationship.Relationship'>, <RelationshipType.SPECIFICATION_FOR: 53>, {})}
def bump_relationships( spdx2_relationships: list[spdx_tools.spdx.model.relationship.Relationship], payload: spdx_tools.spdx3.payload.Payload, document_namespace: str):
153def bump_relationships(
154    spdx2_relationships: List[Spdx2_Relationship],
155    payload: Payload,
156    document_namespace: str,
157):
158    generated_relationships: Dict[Tuple[str, str], List[Relationship]] = {}
159    for counter, spdx2_relationship in enumerate(spdx2_relationships):
160        relationship = bump_relationship(spdx2_relationship, document_namespace, counter)
161        if relationship:
162            generated_relationships.setdefault(
163                (relationship.from_element, relationship.relationship_type.name), []
164            ).append(relationship)
165
166    for relationships in generated_relationships.values():
167        if len(relationships) > 1:
168            _merge_relationships_and_add_to_payload(relationships, payload)
169        else:
170            payload.add_element(relationships[0])
173def bump_relationship(
174    spdx2_relationship: Spdx2_Relationship,
175    document_namespace: str,
176    counter: int,
177) -> Optional[Union[Relationship, SoftwareDependencyRelationship]]:
178    completeness, to = determine_completeness_and_to(spdx2_relationship.related_spdx_element_id)
179    spdx_id = "#".join([document_namespace, f"SPDXRef-Relationship-{counter}"])
180    relationship_class, relationship_type, parameters = relationship_mapping[spdx2_relationship.relationship_type]
181    if relationship_class is None:
182        print_missing_conversion(spdx2_relationship.relationship_type.name, 0)
183        return
184
185    swap_direction = parameters.get("swap", False)
186
187    if swap_direction:
188        if not to:
189            print_missing_conversion("Swapped Relationship to NoAssertion/None", 0)
190            return
191        from_element = to[0]
192        to = [spdx2_relationship.spdx_element_id]
193    else:
194        from_element = spdx2_relationship.spdx_element_id
195
196    if relationship_class == SoftwareDependencyRelationship:
197        from_element = spdx2_relationship.spdx_element_id
198
199        return SoftwareDependencyRelationship(
200            spdx_id,
201            f"{document_namespace}#{from_element}",
202            relationship_type,
203            [f"{document_namespace}#{t}" for t in to],
204            comment=spdx2_relationship.comment,
205            completeness=completeness,
206            scope=parameters.get("scope"),
207            software_linkage=parameters.get("linkage"),
208            conditionality=parameters.get("conditionality"),
209        )
210
211    return Relationship(
212        spdx_id,
213        f"{document_namespace}#{from_element}",
214        relationship_type,
215        [f"{document_namespace}#{t}" for t in to],
216        comment=spdx2_relationship.comment,
217        completeness=completeness,
218    )
def determine_completeness_and_to( related_spdx_element_id: Union[str, spdx_tools.spdx.model.spdx_none.SpdxNone, spdx_tools.spdx.model.spdx_no_assertion.SpdxNoAssertion]) -> tuple[typing.Optional[spdx_tools.spdx3.model.relationship.RelationshipCompleteness], list[str]]:
221def determine_completeness_and_to(
222    related_spdx_element_id: Union[str, SpdxNone, SpdxNoAssertion]
223) -> Tuple[Optional[RelationshipCompleteness], List[str]]:
224    if isinstance(related_spdx_element_id, SpdxNoAssertion):
225        completeness = RelationshipCompleteness.NOASSERTION
226        to = []
227    elif isinstance(related_spdx_element_id, SpdxNone):
228        completeness = RelationshipCompleteness.COMPLETE
229        to = []
230    else:
231        completeness = None
232        to = [related_spdx_element_id]
233    return completeness, to