diff --git a/questionpy_common/api/__init__.py b/questionpy_common/api/__init__.py index 06dc7b4..a8c9ed0 100644 --- a/questionpy_common/api/__init__.py +++ b/questionpy_common/api/__init__.py @@ -1,23 +1,9 @@ # This file is part of QuestionPy. (https://questionpy.org) # QuestionPy is free software released under terms of the MIT license. See LICENSE.md. # (c) Technische Universität Berlin, innoCampus -from collections.abc import ( - Mapping, - Sequence, # noqa: F401 -) -from typing import ( - TypeAlias, - Union, # noqa: F401, -) from pydantic import BaseModel -from typing_extensions import TypeAliasType class Localized(BaseModel): lang: str - - -# "Regular" recursive type aliases break Pydantic: https://github.com/pydantic/pydantic/issues/8346 -PlainValue: TypeAlias = TypeAliasType("PlainValue", "Union[None, int, str, bool, Sequence[PlainValue], PlainMapping]") -PlainMapping: TypeAlias = TypeAliasType("PlainMapping", Mapping[str, PlainValue]) diff --git a/questionpy_common/api/qtype.py b/questionpy_common/api/qtype.py index da473a8..9e33ba3 100644 --- a/questionpy_common/api/qtype.py +++ b/questionpy_common/api/qtype.py @@ -9,9 +9,10 @@ from questionpy_common.api.package import BasePackageInterface if TYPE_CHECKING: + from pydantic import JsonValue + from questionpy_common.elements import OptionsFormDefinition - from . import PlainMapping from .question import QuestionInterface __all__ = ["InvalidQuestionStateError", "OptionsFormValidationError", "QuestionTypeInterface"] @@ -19,7 +20,7 @@ class QuestionTypeInterface(BasePackageInterface, Protocol): @abstractmethod - def get_options_form(self, question_state: str | None) -> tuple[OptionsFormDefinition, PlainMapping]: + def get_options_form(self, question_state: str | None) -> tuple[OptionsFormDefinition, dict[str, JsonValue]]: """Get the form used to create a new or edit an existing question. Args: @@ -30,7 +31,7 @@ def get_options_form(self, question_state: str | None) -> tuple[OptionsFormDefin """ @abstractmethod - def create_question_from_options(self, old_state: str | None, form_data: PlainMapping) -> QuestionInterface: + def create_question_from_options(self, old_state: str | None, form_data: dict[str, JsonValue]) -> QuestionInterface: """Create or update the question (state) with the form data from a submitted question edit form. Args: diff --git a/questionpy_common/api/question.py b/questionpy_common/api/question.py index 53ab036..565c4e8 100644 --- a/questionpy_common/api/question.py +++ b/questionpy_common/api/question.py @@ -5,9 +5,9 @@ from enum import Enum from typing import Annotated, Protocol -from pydantic import BaseModel, Field +from pydantic import BaseModel, Field, JsonValue -from . import Localized, PlainMapping +from . import Localized from .attempt import AttemptModel, AttemptScoredModel, AttemptStartedModel __all__ = ["PossibleResponse", "QuestionInterface", "QuestionModel", "ScoringMethod", "SubquestionModel"] @@ -58,7 +58,7 @@ def start_attempt(self, variant: int) -> AttemptStartedModel: @abstractmethod def get_attempt( - self, attempt_state: str, scoring_state: str | None = None, response: PlainMapping | None = None + self, attempt_state: str, scoring_state: str | None = None, response: dict[str, JsonValue] | None = None ) -> AttemptModel: """Create an attempt object for a previously started attempt. @@ -77,7 +77,7 @@ def score_attempt( self, attempt_state: str, scoring_state: str | None = None, - response: PlainMapping | None = None, + response: dict[str, JsonValue] | None = None, *, try_scoring_with_countback: bool = False, try_giving_hint: bool = False, diff --git a/questionpy_server/worker/runtime/messages.py b/questionpy_server/worker/runtime/messages.py index 30e90a7..4c580a9 100644 --- a/questionpy_server/worker/runtime/messages.py +++ b/questionpy_server/worker/runtime/messages.py @@ -6,9 +6,8 @@ from struct import Struct from typing import Any, ClassVar -from pydantic import BaseModel +from pydantic import BaseModel, JsonValue -from questionpy_common.api import PlainMapping from questionpy_common.api.attempt import AttemptModel, AttemptScoredModel, AttemptStartedModel from questionpy_common.api.qtype import InvalidQuestionStateError from questionpy_common.api.question import QuestionModel @@ -155,7 +154,7 @@ class CreateQuestionFromOptions(MessageToWorker): request_user: RequestUser question_state: str | None """Old question state or ``None`` if the question is new.""" - form_data: PlainMapping + form_data: dict[str, JsonValue] class Response(MessageToServer): message_id: ClassVar[MessageIds] = MessageIds.RETURN_CREATE_QUESTION diff --git a/tests/test_data/package/package_1.qpy b/tests/test_data/package/package_1.qpy index aee7b5a..1926d13 100644 Binary files a/tests/test_data/package/package_1.qpy and b/tests/test_data/package/package_1.qpy differ diff --git a/tests/test_data/package/package_2.qpy b/tests/test_data/package/package_2.qpy index 6a4f7a6..2ba094b 100644 Binary files a/tests/test_data/package/package_2.qpy and b/tests/test_data/package/package_2.qpy differ