Skip to content

Commit

Permalink
Implement language-specific overrides
Browse files Browse the repository at this point in the history
  • Loading branch information
niknetniko committed Jan 29, 2024
1 parent ffc8c56 commit 111defa
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 15 deletions.
124 changes: 109 additions & 15 deletions tested/descriptions/converters.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from abc import ABC, abstractmethod
from functools import partial
from typing import cast
from typing import Callable, cast

from attr import dataclass
from jinja2 import Template
from marko import Markdown
from typing_extensions import override

from tested.configs import Bundle
from tested.datatypes import AdvancedTypes, AllTypes, string_to_type
Expand All @@ -20,11 +22,33 @@
conventionalize_property,
)
from tested.languages.generation import NestedTypeDeclaration, generate_type_declaration
from tested.testsuite import LanguageMapping
from tested.utils import get_args


class Datatype(ABC):
@abstractmethod
def __str__(self) -> str:
raise NotImplementedError()

@property
@abstractmethod
def simple(self) -> str:
raise NotImplementedError()

@property
@abstractmethod
def singular(self) -> str:
raise NotImplementedError()

@property
@abstractmethod
def plural(self) -> str:
raise NotImplementedError()


@dataclass
class Datatype:
class GenericDatatype(Datatype):
locale: str
language: Language
type_: AllTypes
Expand Down Expand Up @@ -58,6 +82,7 @@ def _types(self) -> list[str]:
), f"Could not find concrete type for {self.type_} in {self.language.__class__.__name__}"
return result

@override
def __str__(self) -> str:
types = self._types()
types = [f"`{x}`" for x in types]
Expand All @@ -67,27 +92,83 @@ def __str__(self) -> str:
)

@property
@override
def simple(self) -> str:
if len(self.others):
return generate_type_declaration(self.language, (self.type_, self.others))
else:
return generate_type_declaration(self.language, self.type_)

@property
@override
def singular(self) -> str:
return get_i18n_string(f"types.singular.{self.type_}")

@property
@override
def plural(self) -> str:
return get_i18n_string(f"types.singular.{self.type_}")


def construct_datatype(
locale: str, language: Language, type_: str, *others: NestedTypeDeclaration
@dataclass
class LanguageSpecificDatatype(Datatype):
result: str

@override
def __str__(self) -> str:
return self.result

@property
@override
def simple(self) -> str:
return self.result

@property
@override
def singular(self) -> str:
return self.result

@property
@override
def plural(self) -> str:
return self.result


def _construct_datatype(
bundle: Bundle, type_: str | LanguageMapping, *others: NestedTypeDeclaration
) -> Datatype:
enum_type = string_to_type(type_)
# noinspection PyTypeChecker
return Datatype(locale=locale, language=language, type_=enum_type, others=others)
if isinstance(type_, dict):
assert bundle.config.programming_language in type_, (
f"Cannot convert to {bundle.config.programming_language}, as a language-specific"
f" type was request while not provided. The provided languages are {type_.keys()}."
)
return LanguageSpecificDatatype(
result=type_[bundle.config.programming_language]
)
else:
enum_type = string_to_type(type_)
# noinspection PyTypeChecker
return GenericDatatype(
locale=bundle.config.natural_language,
language=bundle.language,
type_=enum_type,
others=others,
)


def _support_language_specific_arguments(
normal: Callable[[Language, str], str],
bundle: Bundle,
actual: str | LanguageMapping,
) -> str:
if isinstance(actual, dict):
assert bundle.config.programming_language in actual, (
f"Cannot convert to {bundle.config.programming_language}, as a language-specific"
f" construct was request while not provided. The provided languages are {actual.keys()}."
)
return actual[bundle.config.programming_language]
else:
return normal(bundle.language, actual)


def convert_templated_problem(bundle: Bundle, raw_description: str) -> str:
Expand All @@ -102,20 +183,33 @@ def convert_templated_problem(bundle: Bundle, raw_description: str) -> str:
description_template = Template(
source=raw_description, autoescape=False, keep_trailing_newline=True
)
language = bundle.language
set_locale(bundle.config.natural_language)
return description_template.render(
# Conventionalize functions
namespace=partial(conventionalize_namespace, language),
function=partial(conventionalize_function, language),
identifier=partial(conventionalize_identifier, language),
property=partial(conventionalize_property, language),
clazz=partial(conventionalize_class, language),
global_identifier=partial(conventionalize_global_identifier, language),
namespace=partial(
_support_language_specific_arguments, conventionalize_namespace, bundle
),
function=partial(
_support_language_specific_arguments, conventionalize_function, bundle
),
identifier=partial(
_support_language_specific_arguments, conventionalize_identifier, bundle
),
property=partial(
_support_language_specific_arguments, conventionalize_property, bundle
),
clazz=partial(
_support_language_specific_arguments, conventionalize_class, bundle
),
global_identifier=partial(
_support_language_specific_arguments,
conventionalize_global_identifier,
bundle,
),
# Access to the current programming language
programming_language=bundle.config.programming_language,
# Data type conversion
datatype=partial(construct_datatype, bundle.config.natural_language, language),
datatype=partial(_construct_datatype, bundle),
t=partial(render_one_statement, bundle),
)

Expand Down
21 changes: 21 additions & 0 deletions tests/test_problem_statements.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,3 +206,24 @@ def test_long_description():
actual = process_problem_statement(template, "python")

assert actual == expected


def test_language_specific_construct_works():
template = r"{{ datatype({'python': 'result'}) }}"
actual = process_problem_statement(template, "python")
assert actual == "result"

template = r"{{ datatype({'python': 'result'}).singular }}"
actual = process_problem_statement(template, "python")
assert actual == "result"

template = r"{{ datatype({'python': 'result'}).plural }}"
actual = process_problem_statement(template, "python")
assert actual == "result"


def test_language_specific_construct_rejects_missing():
template = r"{{ datatype({'python': 'result'}) }}"

with pytest.raises(AssertionError):
process_problem_statement(template, "java")

0 comments on commit 111defa

Please sign in to comment.