Skip to content

abstract.py

ofrak.component.abstract

AbstractComponent (ComponentInterface, ABC)

run(self, job_id, resource_id, job_context, resource_context, resource_view_context, config) async

Parameters:

Name Type Description Default
job_id bytes required
resource_id bytes required
job_context JobRunContext required
resource_context ResourceContext required
resource_view_context ResourceViewContext required
config ~CC required

Returns:

Type Description
ComponentRunResult

The IDs of all resources modified by this component

Source code in ofrak/component/abstract.py
async def run(
    self,
    job_id: bytes,
    resource_id: bytes,
    job_context: JobRunContext,
    resource_context: ResourceContext,
    resource_view_context: ResourceViewContext,
    config: CC,
) -> ComponentRunResult:
    """

    :param job_id:
    :param resource_id:
    :param job_context:
    :param resource_context:
    :param resource_view_context:
    :param config:
    :return: The IDs of all resources modified by this component
    """
    component_context = ComponentContext(self.get_id(), self.get_version())
    resource = await self._resource_factory.create(
        job_id,
        resource_id,
        resource_context,
        resource_view_context,
        component_context,
        job_context,
    )
    if config is None and self._default_config is not None:
        config = dataclasses.replace(self._default_config)
    try:
        await self._run(resource, config)
    except FileNotFoundError as e:
        # Check if the problem was that one of the dependencies is missing
        missing_file = e.filename
        for dep in self.external_dependencies:
            if missing_file:
                if dep.tool == missing_file:
                    raise ComponentMissingDependencyError(self, dep)
            # on Windows a filename is not provided from subprocess FileNotFoundError, so just
            # assume the any missing tool we find is the problem
            elif not await dep.is_tool_installed():
                raise ComponentMissingDependencyError(self, dep)

        raise
    except CalledProcessError as e:
        raise ComponentSubprocessError(e)

    deleted_resource_models: List[MutableResourceModel] = list()

    for deleted_r_id in component_context.resources_deleted:
        mutable_resource_model = resource_context.resource_models.get(deleted_r_id)
        if mutable_resource_model:
            deleted_resource_models.append(mutable_resource_model)
        else:
            raise NotFoundError(
                f"The resource {deleted_r_id.hex()} was deleted but not in "
                f"the resource context"
            )

    # Save deleted resource so they won't interfere with patches
    # This is where deleted resources are actually deleted from their respective databases
    await self._save_resources(
        job_id,
        deleted_resource_models,
        resource_context,
        resource_view_context,
        job_context,
        component_context,
    )

    dependency_handler = self._dependency_handler_factory.create(
        self._resource_service,
        self._data_service,
        component_context,
        resource_context,
    )
    patch_results = await self.apply_all_patches(component_context)
    await dependency_handler.handle_post_patch_dependencies(patch_results)
    dependency_handler.create_component_dependencies(self.get_id(), self.get_version())
    dependency_handler.create_resource_dependencies(self.get_id())

    # Get modified resources
    modified_resource_models: Dict[bytes, MutableResourceModel] = dict()
    modified_resource_ids = component_context.get_modified_resource_ids()
    for modified_r_id in modified_resource_ids:
        mutable_resource_model = resource_context.resource_models.get(modified_r_id)
        if mutable_resource_model:
            modified_resource_models[modified_r_id] = mutable_resource_model
        else:
            raise NotFoundError(
                f"The resource {modified_r_id.hex()} was modified but not in "
                f"the resource context"
            )

    # Include resources modified by data patches in `modified_resource_ids`
    data_ids_to_models = await dependency_handler.map_data_ids_to_resources(
        patch_result.data_id for patch_result in patch_results
    )
    for m in data_ids_to_models.values():
        modified_resource_ids.add(m.id)

    # Exclude deleted resources from `modified_resource_ids`
    # (deleting and modifying are handled separately)
    modified_resource_ids.difference_update(component_context.resources_deleted)

    # Save modified resources
    await self._save_resources(
        job_id,
        modified_resource_models.values(),
        resource_context,
        resource_view_context,
        job_context,
        component_context,
    )

    component_result = ComponentRunResult(
        {self.get_id()},
        modified_resource_ids,
        component_context.resources_deleted,
        component_context.resources_created,
    )
    return component_result