Source code for glotter_core.testinfo

"""Container, folder, and test information"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Optional

import yaml
from jinja2 import BaseLoader, Environment

from .project import CoreProjectMixin, NamingScheme


[docs] @dataclass(frozen=True) class ContainerInfo: """Configuration for a container to run for a directory :ivar image: the image to run :ivar tag: the tag of the image to run :ivar cmd: the command to run the source inside the container :ivar build: an optional command to run to build the source before running the command """ image: str = "" tag: str = "" cmd: str = "" build: Optional[str] = None
[docs] @classmethod def from_dict(cls, dictionary: dict[str, Optional[str]]) -> ContainerInfo: """ Create a ContainerInfo object from a dictionary :param dictionary: the dictionary representing ContainerInfo :return: a new ContainerInfo object """ image = dictionary.get("image", "") tag = dictionary.get("tag", "") cmd = dictionary.get("cmd", "") build = dictionary.get("build") return ContainerInfo(image=image, tag=tag, cmd=cmd, build=build)
def __bool__(self) -> bool: return bool(self.image and self.tag and self.cmd)
[docs] @dataclass(frozen=True) class FolderInfo: """Metadata about sources in a directory :param extension: the file extension that is considered as source :param str naming: string containing the naming scheme for files in the directory :raises: :exc:`ValueError` if invalid naming scheme :ivar extension: the file extension that is considered as source :ivar NamingScheme naming: the naming scheme for files in the directory """ extension: str naming: str def __post_init__(self) -> None: try: object.__setattr__(self, "naming", NamingScheme[self.naming]) except KeyError as e: raise ValueError(f'Unknown naming scheme: "{self.naming}"') from e
[docs] def get_project_mappings( self, projects: dict[str, CoreProjectMixin], include_extension: bool = False ) -> dict[str, str]: """ Uses the naming scheme to generate the expected source names in the directory and create a mapping from project type to source name :param project: dictionary whose key is a project type and whose value is information about the project :param include_extension: whether to include the extension in the source name :return: a dict where the key is a project type and the value is the source name """ extension = self.extension if include_extension else "" return { project_type: f"{project.get_project_name_by_scheme(self.naming)}{extension}" for project_type, project in projects.items() }
[docs] @classmethod def from_dict(cls, dictionary: dict[str, str]) -> FolderInfo: """ Create a FileInfo from a dictionary :param dictionary: the dictionary representing FileInfo :return: a new FileInfo """ return FolderInfo(dictionary["extension"], dictionary["naming"])
[docs] @dataclass(frozen=True) class TestInfo: """An object representation of a testinfo file :param container_info: ContainerInfo object :param file_info: FolderInfo object :param language_display_name: string indicating the display name of the language :param notes: a list of notes about the language :ivar container_info: ContainerInfo object :ivar file_info: FolderInfo object :ivar language_display_name: string indicating the display name of the language :ivar notes: a list of notes about the language """ container_info: ContainerInfo file_info: FolderInfo language_display_name: str notes: list[str] = field(default_factory=list) __test__ = False # Indicate this is not a test
[docs] @classmethod def from_dict(cls, dictionary: dict[str, Any], language: str) -> TestInfo: """ Create a TestInfo object from a dictionary :param dictionary: the dictionary representing a TestInfo object :param language: language of source object :return: a new TestInfo object """ language_display_name = dictionary.get( "language_display_name", _get_language_display_name(language) ) return TestInfo( container_info=ContainerInfo.from_dict(dictionary.get("container", {})), file_info=FolderInfo.from_dict(dictionary["folder"]), language_display_name=language_display_name, notes=dictionary.get("notes", []), )
[docs] @classmethod def from_string(cls, string: str, source) -> TestInfo: """ Create a TestInfo from a string. Modify the string using Jinja2 templating. Then parse it as yaml :param string: contents of a testinfo file :param source: a source object to use for jinja2 template parsing :param language: language of source :return: a new TestInfo """ template = Environment(loader=BaseLoader).from_string(string) template_string = template.render(source=source) info_yaml = yaml.safe_load(template_string) return cls.from_dict(info_yaml, source.language)
@property def is_testable(self) -> bool: """ Indicate if language is testable :return: True if language is testable, False otherwise """ return bool(self.container_info)
LANGUAGE_TEXT_TO_SYMBOL = {"plus": "+", "sharp": "#", "star": "*"} def _get_language_display_name(language: str) -> str: tokens = [LANGUAGE_TEXT_TO_SYMBOL.get(token, token) for token in language.split("-")] separator = " " if any(token in LANGUAGE_TEXT_TO_SYMBOL.values() for token in tokens): separator = "" return separator.join(tokens).title() __all__ = ["ContainerInfo", "FolderInfo", "TestInfo"]