Repository: Pythagora-io/gpt-pilot
File: pilot/helpers/files.py (Python)

Code before:
        print(color_green(f"Updated file {path}"))


def get_file_contents(
    path: str, project_root_path: str
) -> Dict[str, Union[str, bytes]]:
    """
    Get file content and metadata.

    :param path: Full path to the file
    :param project_root_path: Full path to the project root directory
    :return: Object with the following keys:
        - name: File name
        - path: Relative path to the file
        - content: File content (str or bytes)
        - full_path: Full path to the file

    If file is text, it will be read using UTF-8 encoding and `content`
    will be a Python string. If that fails, it will be treated as a
    binary file and `content` will be a Python bytes object.
    """
    # Normalize the path to avoid issues with different path separators
    full_path = os.path.normpath(os.path.join(project_root_path, path))

    # Check if the path is a file
    if not os.path.isfile(full_path):
        raise ValueError(f"The path provided is not a file: {full_path}")

    try:
        # Assume it's a text file using UTF-8 encoding
        with open(full_path, "r", encoding="utf-8") as file:
            file_content = file.read()
    except UnicodeDecodeError:
        # If that fails, we'll treat it as a binary file
        with open(full_path, "rb") as file:
            file_content = file.read()
    except FileNotFoundError:
        raise ValueError(f"File not found: {full_path}")

    file_name = os.path.basename(full_path)
    relative_path = os.path.relpath(os.path.dirname(full_path), project_root_path)

    if relative_path == '.':
        relative_path = ''

    return {
        "name": file_name,
        "path": relative_path,
        "content": file_content,
        "full_path": full_path,
    }

Review comment (security):
It'd be better to do this as a general `except Exception` below so it can catch any other errors that might happen (eg. permission error, etc).

Code after:
        print(color_green(f"Updated file {path}"))


def get_file_contents(
    path: str, project_root_path: str
) -> dict[str, Union[str, bytes]]:
    """
    Get file content and metadata.

    :param path: Full path to the file
    :param project_root_path: Full path to the project root directory
    :return: Object with the following keys:
        - name: File name
        - path: Relative path to the file
        - content: File content (str or bytes)
        - full_path: Full path to the file

    If file is text, it will be read using UTF-8 encoding and `content`
    will be a Python string. If that fails, it will be treated as a
    binary file and `content` will be a Python bytes object.
    """
    # Normalize the path to avoid issues with different path separators
    full_path = os.path.normpath(path)

    try:
        # Assume it's a text file using UTF-8 encoding
        with open(full_path, "r", encoding="utf-8") as file:
            file_content = file.read()
    except UnicodeDecodeError:
        # If that fails, we'll treat it as a binary file
        with open(full_path, "rb") as file:
            file_content = file.read()
    except NotADirectoryError:
        raise ValueError(f"Path is not a directory: {path}")
    except FileNotFoundError:
        raise ValueError(f"File not found: {full_path}")
    except Exception as e:
        raise ValueError(f"Exception in get_file_contents: {e}")

    file_name = os.path.basename(path)
    relative_path = str(Path(path).parent.relative_to(project_root_path))

    if relative_path == '.':
        relative_path = ''

    return {
        "name": file_name,
        "path": relative_path,
        "content": file_content,
        "full_path": full_path,
    }