Source code for ankaios_sdk._components.file

# Copyright (c) 2025 Elektrobit Automotive GmbH
#
# This program and the accompanying materials are made available under the
# terms of the Apache License, Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0.
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# SPDX-License-Identifier: Apache-2.0

"""
This module defines the File class for
handling files mounted to Ankaios workloads.

Classes
-------

- File:
    Represents a file that can be mounted to an Ankaios workload.
- DataFileContent:
    Represents text-based file content.
- BinaryFileContent:
    Represents binary file content.

Union Types
-----------

- FileContent:
    Union type for file content, which can be either
    :py:class:`DataFileContent` or :py:class:`BinaryFileContent`.

Usage
-----

- Create a File instance from text data:
    .. code-block:: python

        file = File.from_data(
                    mount_point="/path/to/mount",
                    data="file content")

- Create a File instance from binary data:
    .. code-block:: python

        file = File.from_binary_data(
                    mount_point="/path/to/mount",
                    binary_data="binary content")

- Convert a File instance to a dictionary:
    .. code-block:: python

        file_dict = file.to_dict()

- Get the content and check it's type:
    .. code-block:: python

        file = File()
        if isinstance(file.content, DataFileContent):
            print("Text file content:", file.content.value)
        elif isinstance(file.content, BinaryFileContent):
            print("Binary file content:", file.content.value)
"""


__all__ = ["File", "DataFileContent", "BinaryFileContent"]


from dataclasses import dataclass
from typing import Union
from .._protos import _ank_base


[docs] @dataclass class DataFileContent: """ This class is used to represent text-based file content. Attributes: value (str): The text content value. """ value: str
[docs] @dataclass class BinaryFileContent: """ This class is used to represent binary file content. Attributes: value (str): The binary content value as a `Base64`_ encoded string. .. _Base64: https://datatracker.ietf.org/doc/html/rfc4648 """ value: str
FileContent = Union[DataFileContent, BinaryFileContent]
[docs] class File: """ This class represents a file able to be mounted to an Ankaios workload. It can hold either text-based or binary content. Attributes: mount_point (str): The mount point of the file. content (FileContent): The content of the file, which can be either text or binary. """
[docs] def __init__(self, mount_point: str, content: FileContent) -> None: """ Initialize a File instance. Args: mount_point (str): The mount point of the file. content (FileContent): The content of the file, which can be either text or binary. """ self.mount_point = mount_point self.content: FileContent = content
[docs] @classmethod def from_data(cls, mount_point: str, data: str) -> "File": """ Create a File instance from text data. Args: mount_point (str): The mount point of the file. data (str): The text content of the file. Returns: File: A File instance with text-based content. """ return cls( mount_point=mount_point, content=DataFileContent(value=data) )
[docs] @classmethod def from_binary_data(cls, mount_point: str, binary_data: str) -> "File": """ Create a File instance from binary data. Args: mount_point (str): The mount point of the file. binary_data (str): The binary content of the file. Returns: File: A File instance with binary content. """ return cls( mount_point=mount_point, content=BinaryFileContent(value=binary_data), )
[docs] def __str__(self) -> str: """ Return a string representation of the File object. Returns: str: String representation of the File object. """ return str(self._to_proto())
[docs] def to_dict(self) -> dict: """ Convert the File instance to a dictionary representation. Returns: dict: The dictionary representation of the File instance. Raises: ValueError: If the file content type is unsupported. """ dict_conv = {"mount_point": self.mount_point} if isinstance(self.content, DataFileContent): dict_conv["content"] = {"data": self.content.value} elif isinstance(self.content, BinaryFileContent): dict_conv["content"] = {"binaryData": self.content.value} else: # pragma: no cover raise ValueError( "Unsupported file content type. " "Expected Data or BinaryData." ) return dict_conv
@staticmethod def _from_dict(file_dict: dict) -> "File": """ Create a File instance from a dictionary representation. Args: file_dict (dict): The dictionary containing file information. Returns: File: A File instance created from the dictionary. Raises: ValueError: If the file dictionary format is invalid. """ mount_point = file_dict.get("mount_point") content = file_dict.get("content") if not content: raise ValueError( "Invalid file dictionary format. Expected 'content' key." ) if content.get("data"): return File.from_data( mount_point=mount_point, data=file_dict["content"]["data"] ) if content.get("binaryData"): return File.from_binary_data( mount_point=mount_point, binary_data=file_dict["content"]["binaryData"], ) # Unreachable code, as the content must be either data or binaryData. raise ValueError( "Invalid file dictionary format. " "Expected 'data' or 'binaryData' key." ) # pragma: no cover def _to_proto(self) -> _ank_base.File: """ Convert the File instance to a protobuf representation. Returns: _ank_base.File: The protobuf representation of the File instance. Raises: ValueError: If the file content type is unsupported. """ if isinstance(self.content, DataFileContent): return _ank_base.File( mountPoint=self.mount_point, data=self.content.value ) if isinstance(self.content, BinaryFileContent): return _ank_base.File( mountPoint=self.mount_point, binaryData=self.content.value ) # Unreachable code, as the content type # is checked in the methods above. raise ValueError( "Unsupported file content type. Expected Data or BinaryData." ) # pragma: no cover @staticmethod def _from_proto(proto_file: _ank_base.File) -> "File": """ Create a File instance from a protobuf representation. Args: proto_file (_ank_base.File): The protobuf file representation. Returns: File: A File instance created from the protobuf representation. Raises: ValueError: If the protobuf file format is invalid. """ if proto_file.data: return File.from_data( mount_point=proto_file.mountPoint, data=proto_file.data ) if proto_file.binaryData: return File.from_binary_data( mount_point=proto_file.mountPoint, binary_data=proto_file.binaryData, ) # Unreachable code, as the protobuf # should always have one of these fields. raise ValueError( "Invalid protobuf file format. " "Expected 'data' or 'binaryData' field." ) # pragma: no cover