diff --git a/docs/docs/resources/pipeline-components/dependencies/kpops_structure.yaml b/docs/docs/resources/pipeline-components/dependencies/kpops_structure.yaml index 21af5971c..5566d7ba3 100644 --- a/docs/docs/resources/pipeline-components/dependencies/kpops_structure.yaml +++ b/docs/docs/resources/pipeline-components/dependencies/kpops_structure.yaml @@ -18,6 +18,7 @@ kpops_components_fields: - repo_config - version - resetter_values + - connector_type kafka-sink-connector: - name - prefix @@ -28,6 +29,7 @@ kpops_components_fields: - repo_config - version - resetter_values + - connector_type kafka-source-connector: - name - prefix @@ -38,6 +40,7 @@ kpops_components_fields: - repo_config - version - resetter_values + - connector_type - offset_topic kubernetes-app: - name diff --git a/docs/docs/schema/pipeline.json b/docs/docs/schema/pipeline.json index f7ec539c5..4653f57c5 100644 --- a/docs/docs/schema/pipeline.json +++ b/docs/docs/schema/pipeline.json @@ -122,7 +122,16 @@ "title": "KafkaConnectorConfig", "type": "object" }, + "KafkaConnectorType": { + "enum": [ + "sink", + "source" + ], + "title": "KafkaConnectorType", + "type": "string" + }, "KafkaSinkConnector": { + "additionalProperties": true, "description": "Kafka sink connector model.", "properties": { "app": { @@ -133,6 +142,14 @@ ], "description": "Application-specific settings" }, + "connector_type": { + "allOf": [ + { + "$ref": "#/definitions/KafkaConnectorType" + } + ], + "default": "sink" + }, "from": { "anyOf": [ { @@ -190,21 +207,23 @@ "anyOf": [ { "$ref": "#/definitions/ToSection" + }, + { + "type": "null" } ], - "description": "Topic(s) into which the component will write output", - "title": "To" - }, - "type": { - "default": "kafka-sink-connector", - "description": "Kafka sink connector model.", - "enum": [ - "kafka-sink-connector" - ], - "title": "Component type", - "type": "string" + "default": null, + "description": "Topic(s) into which the component will write output" }, "version": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], "default": "1.0.4", "description": "Helm chart version", "title": "Version" @@ -219,6 +238,7 @@ "type": "object" }, "KafkaSourceConnector": { + "additionalProperties": true, "description": "Kafka source connector model.", "properties": { "app": { @@ -229,6 +249,14 @@ ], "description": "Application-specific settings" }, + "connector_type": { + "allOf": [ + { + "$ref": "#/definitions/KafkaConnectorType" + } + ], + "default": "source" + }, "from": { "anyOf": [ { @@ -299,21 +327,23 @@ "anyOf": [ { "$ref": "#/definitions/ToSection" + }, + { + "type": "null" } ], - "description": "Topic(s) into which the component will write output", - "title": "To" - }, - "type": { - "default": "kafka-source-connector", - "description": "Kafka source connector model.", - "enum": [ - "kafka-source-connector" - ], - "title": "Component type", - "type": "string" + "default": null, + "description": "Topic(s) into which the component will write output" }, "version": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], "default": "1.0.4", "description": "Helm chart version", "title": "Version" @@ -369,11 +399,15 @@ "type": "string" }, "repo_config": { - "allOf": [ + "anyOf": [ { "$ref": "#/definitions/HelmRepoConfig" + }, + { + "type": "null" } ], + "default": null, "description": "Configuration of the Helm chart repo to be used for deploying the component" }, "to": { @@ -411,6 +445,7 @@ "type": "object" }, "KubernetesAppConfig": { + "additionalProperties": true, "description": "Settings specific to Kubernetes Apps.", "properties": {}, "title": "KubernetesAppConfig", @@ -426,6 +461,7 @@ "type": "string" }, "ProducerApp": { + "additionalProperties": true, "description": "Producer component.\nThis producer holds configuration to use as values for the streams bootstrap producer helm chart. Note that the producer does not support error topics.", "properties": { "app": { @@ -481,21 +517,23 @@ "anyOf": [ { "$ref": "#/definitions/ToSection" + }, + { + "type": "null" } ], - "description": "Topic(s) into which the component will write output", - "title": "To" - }, - "type": { - "default": "producer-app", - "description": "Producer component.\nThis producer holds configuration to use as values for the streams bootstrap producer helm chart. Note that the producer does not support error topics.", - "enum": [ - "producer-app" - ], - "title": "Component type", - "type": "string" + "default": null, + "description": "Topic(s) into which the component will write output" }, "version": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], "default": "2.9.0", "description": "Helm chart version", "title": "Version" @@ -510,6 +548,7 @@ "type": "object" }, "ProducerStreamsConfig": { + "additionalProperties": true, "description": "Kafka Streams settings specific to Producer.", "properties": { "brokers": { @@ -560,6 +599,7 @@ "type": "object" }, "ProducerValues": { + "additionalProperties": true, "description": "Settings specific to producers.", "properties": { "nameOverride": { @@ -658,6 +698,7 @@ "type": "object" }, "StreamsApp": { + "additionalProperties": true, "description": "StreamsApp component that configures a streams bootstrap app.", "properties": { "app": { @@ -720,21 +761,23 @@ "anyOf": [ { "$ref": "#/definitions/ToSection" + }, + { + "type": "null" } ], - "description": "Topic(s) into which the component will write output", - "title": "To" - }, - "type": { - "default": "streams-app", - "description": "StreamsApp component that configures a streams bootstrap app.", - "enum": [ - "streams-app" - ], - "title": "Component type", - "type": "string" + "default": null, + "description": "Topic(s) into which the component will write output" }, "version": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], "default": "2.9.0", "description": "Helm chart version", "title": "Version" @@ -749,6 +792,7 @@ "type": "object" }, "StreamsAppAutoScaling": { + "additionalProperties": true, "description": "Kubernetes Event-driven Autoscaling config.", "properties": { "consumerGroup": { @@ -857,7 +901,12 @@ "title": "Nameoverride" }, "streams": { - "$ref": "#/definitions/StreamsConfig" + "allOf": [ + { + "$ref": "#/definitions/StreamsConfig" + } + ], + "description": "Streams Bootstrap streams section" } }, "required": [ @@ -867,121 +916,12 @@ "type": "object" }, "StreamsConfig": { - "description": "Streams Bootstrap streams section.", - "properties": { - "brokers": { - "description": "Brokers", - "title": "Brokers", - "type": "string" - }, - "config": { - "additionalProperties": { - "type": "string" - }, - "default": {}, - "description": "Configuration", - "title": "Config", - "type": "object" - }, - "errorTopic": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Error topic", - "title": "Errortopic" - }, - "extraInputPatterns": { - "additionalProperties": { - "type": "string" - }, - "default": {}, - "description": "Extra input patterns", - "title": "Extrainputpatterns", - "type": "object" - }, - "extraInputTopics": { - "additionalProperties": { - "items": { - "type": "string" - }, - "type": "array" - }, - "default": {}, - "description": "Extra input topics", - "title": "Extrainputtopics", - "type": "object" - }, - "extraOutputTopics": { - "additionalProperties": { - "type": "string" - }, - "default": {}, - "description": "Extra output topics", - "title": "Extraoutputtopics", - "type": "object" - }, - "inputPattern": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Input pattern", - "title": "Inputpattern" - }, - "inputTopics": { - "default": [], - "description": "Input topics", - "items": { - "type": "string" - }, - "title": "Inputtopics", - "type": "array" - }, - "outputTopic": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "Output topic", - "title": "Outputtopic" - }, - "schemaRegistryUrl": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "description": "URL of the schema registry", - "title": "Schemaregistryurl" - } - }, - "required": [ - "brokers" - ], + "description": "Streams Bootstrap streams section.\n\n:param input_topics: Input topics, defaults to []\n:param input_pattern: Input pattern, defaults to None\n:param extra_input_topics: Extra input topics, defaults to {}\n:param extra_input_patterns: Extra input patterns, defaults to {}\n:param extra_output_topics: Extra output topics, defaults to {}\n:param output_topic: Output topic, defaults to None\n:param error_topic: Error topic, defaults to None\n:param config: Configuration, defaults to {}", "title": "StreamsConfig", "type": "object" }, "ToSection": { + "additionalProperties": false, "description": "Holds multiple output topics.", "properties": { "models": { diff --git a/hooks/gen_docs/gen_docs_components.py b/hooks/gen_docs/gen_docs_components.py index 3fffd8c7b..ebd7353f2 100644 --- a/hooks/gen_docs/gen_docs_components.py +++ b/hooks/gen_docs/gen_docs_components.py @@ -44,8 +44,8 @@ KPOPS_COMPONENTS_SECTIONS = { component.type: [ field_name - for field_name, model in component.__fields__.items() # pyright: ignore[reportGeneralTypeIssues] - if not model.exclude + for field_name, field_info in component.model_fields.items() # pyright: ignore[reportGeneralTypeIssues] + if not field_info.exclude ] for component in KPOPS_COMPONENTS } diff --git a/hooks/gen_docs/gen_docs_env_vars.py b/hooks/gen_docs/gen_docs_env_vars.py index 6ac7b2adc..b4d22fff9 100644 --- a/hooks/gen_docs/gen_docs_env_vars.py +++ b/hooks/gen_docs/gen_docs_env_vars.py @@ -6,10 +6,10 @@ from dataclasses import dataclass from pathlib import Path from textwrap import fill -from typing import Any +from typing import Any, get_args +from pydantic.fields import FieldInfo from pydantic_settings import BaseSettings -from pydantic.fields import FieldInfo from pytablewriter import MarkdownTableWriter from typer.models import ArgumentInfo, OptionInfo @@ -269,16 +269,18 @@ def fill_csv_pipeline_config(target: Path) -> None: ) -def collect_fields(settings: type[BaseSettings]) -> Iterator[FieldInfo]: +def collect_fields(settings: type[BaseSettings]) -> Iterator[tuple[str, FieldInfo]]: """Collect and yield all fields in a settings class. :param model: settings class :yield: all settings including nested ones in settings classes """ - for field in settings.model_fields.values(): - if field.annotation and issubclass(field_type := field.annotation, BaseSettings): - yield from collect_fields(field_type) - yield field + for field_name, field_value in settings.model_fields.items(): + if field_value.annotation: + for field_type in get_args(field_value.annotation): + if field_type and issubclass(field_type, BaseSettings): + yield from collect_fields(field_type) + yield field_name, field_value def fill_csv_cli(target: Path) -> None: diff --git a/kpops/cli/pipeline_config.py b/kpops/cli/pipeline_config.py index ad66dfb27..61538c1ce 100644 --- a/kpops/cli/pipeline_config.py +++ b/kpops/cli/pipeline_config.py @@ -1,7 +1,6 @@ from __future__ import annotations from pathlib import Path -from typing import TYPE_CHECKING, Any from pydantic import AliasChoices, Field from pydantic_settings import BaseSettings, PydanticBaseSettingsSource @@ -9,11 +8,6 @@ from kpops.cli.settings_sources import YamlConfigSettingsSource from kpops.component_handlers.helm_wrapper.model import HelmConfig, HelmDiffConfig -if TYPE_CHECKING: - from collections.abc import Callable - - from pydantic.env_settings import SettingsSourceCallable - ENV_PREFIX = "KPOPS_" @@ -34,7 +28,7 @@ class PipelineConfig(BaseSettings): """Pipeline configuration unrelated to the components.""" defaults_path: Path = Field( - default=Path("."), + default=Path(), examples=["defaults", "."], description="The path to the folder containing the defaults.yaml file and the environment defaults files. " "Paths can either be absolute or relative to `config.yaml`", @@ -121,9 +115,9 @@ def settings_customise_sources( file_secret_settings: PydanticBaseSettingsSource, ): return ( + env_settings, init_settings, YamlConfigSettingsSource(settings_cls), dotenv_settings, - env_settings, file_secret_settings, ) diff --git a/kpops/component_handlers/helm_wrapper/model.py b/kpops/component_handlers/helm_wrapper/model.py index 66100d7ef..0a155bb0d 100644 --- a/kpops/component_handlers/helm_wrapper/model.py +++ b/kpops/component_handlers/helm_wrapper/model.py @@ -20,7 +20,7 @@ class HelmDiffConfig(BaseModel): class RepoAuthFlags(DescConfigModel): - """Authorisation-related flags for `helm repo` + """Authorisation-related flags for `helm repo`. :param username: Username, defaults to None :param password: Password, defaults to None @@ -62,7 +62,7 @@ def to_command(self) -> list[str]: class HelmRepoConfig(DescConfigModel): - """Helm repository configuration + """Helm repository configuration. :param repository_name: Name of the Helm repository :param url: URL to the Helm repository @@ -79,7 +79,7 @@ class HelmRepoConfig(DescConfigModel): class HelmConfig(DescConfigModel): - """Global Helm configuration + """Global Helm configuration. :param context: Name of kubeconfig context (`--kube-context`) :param debug: Run Helm in Debug mode diff --git a/kpops/component_handlers/kafka_connect/connect_wrapper.py b/kpops/component_handlers/kafka_connect/connect_wrapper.py index aba8b2fc1..61e9def54 100644 --- a/kpops/component_handlers/kafka_connect/connect_wrapper.py +++ b/kpops/component_handlers/kafka_connect/connect_wrapper.py @@ -62,8 +62,7 @@ def create_connector( raise KafkaConnectError(response) def get_connector(self, connector_name: str | None) -> KafkaConnectResponse: - """ - Get information about the connector. + """Get information about the connector. API Reference: https://docs.confluent.io/platform/current/connect/references/restapi.html#get--connectors-(string-name) :param connector_name: Nameof the crated connector :return: Information about the connector. diff --git a/kpops/component_handlers/kafka_connect/model.py b/kpops/component_handlers/kafka_connect/model.py index 9efeebdeb..c79dd8dc6 100644 --- a/kpops/component_handlers/kafka_connect/model.py +++ b/kpops/component_handlers/kafka_connect/model.py @@ -2,8 +2,7 @@ from typing import Any, Literal from pydantic import BaseModel, ConfigDict, Field, field_validator -from pydantic.json_schema import WithJsonSchema -from typing_extensions import Annotated, override +from typing_extensions import override from kpops.utils.pydantic import CamelCaseConfigModel, DescConfigModel, to_dot @@ -14,13 +13,10 @@ class KafkaConnectorType(str, Enum): class KafkaConnectorConfig(DescConfigModel): - """Settings specific to Kafka Connectors""" + """Settings specific to Kafka Connectors.""" connector_class: str - name: str | None = Field( - default=None, - exclude=True, - ) + name: str | None = Field(default=None) model_config = ConfigDict( extra="allow", alias_generator=to_dot, diff --git a/kpops/components/base_components/base_defaults_component.py b/kpops/components/base_components/base_defaults_component.py index 17e780e58..e8007fd19 100644 --- a/kpops/components/base_components/base_defaults_component.py +++ b/kpops/components/base_components/base_defaults_component.py @@ -8,8 +8,6 @@ import typer from pydantic import AliasChoices, ConfigDict, Field -from pydantic.json_schema import WithJsonSchema -from typing_extensions import Annotated from kpops.cli.pipeline_config import PipelineConfig from kpops.component_handlers import ComponentHandlers diff --git a/kpops/components/base_components/kafka_app.py b/kpops/components/base_components/kafka_app.py index 9d277a4f7..e0430db80 100644 --- a/kpops/components/base_components/kafka_app.py +++ b/kpops/components/base_components/kafka_app.py @@ -22,7 +22,7 @@ class KafkaStreamsConfig(CamelCaseConfigModel, DescConfigModel): - """Kafka Streams config + """Kafka Streams config. :param brokers: Brokers :param schema_registry_url: URL of the schema registry, defaults to None diff --git a/kpops/components/base_components/kafka_connector.py b/kpops/components/base_components/kafka_connector.py index a03fe7dc0..c70dccb58 100644 --- a/kpops/components/base_components/kafka_connector.py +++ b/kpops/components/base_components/kafka_connector.py @@ -73,7 +73,8 @@ class KafkaConnector(PipelineComponent, ABC): default_factory=dict, description=describe_attr("resetter_values", __doc__), ) - connector_type: KafkaConnectorType = Field() + _connector_type: KafkaConnectorType + @field_validator("app") @classmethod def connector_config_should_have_component_name( @@ -89,8 +90,7 @@ def connector_config_should_have_component_name( msg = "Connector name should be the same as component name" raise ValueError(msg) app["name"] = component_name - app = KafkaConnectorConfig(**app) - return app + return KafkaConnectorConfig(**app) @cached_property def helm(self) -> Helm: @@ -190,7 +190,7 @@ def _run_connect_resetter( log.info( magentaify( - f"Connector Cleanup: deploy Connect {self.connector_type.value} resetter for {self.full_name}" + f"Connector Cleanup: deploy Connect {self._connector_type.value} resetter for {self.full_name}" ) ) @@ -246,7 +246,7 @@ def _get_kafka_connect_resetter_values( brokers=self.config.brokers, **kwargs, ), - connector_type=self.connector_type.value, + connector_type=self._connector_type.value, name_override=self.full_name, ).model_dump(), **self.resetter_values, @@ -278,7 +278,7 @@ class KafkaSourceConnector(KafkaConnector): description=describe_attr("offset_topic", __doc__), ) - connector_type: KafkaConnectorType = KafkaConnectorType.SOURCE + _connector_type: KafkaConnectorType = KafkaConnectorType.SOURCE @override def apply_from_inputs(self, name: str, topic: FromTopic) -> NoReturn: @@ -323,7 +323,7 @@ def __run_kafka_connect_resetter(self, dry_run: bool) -> None: class KafkaSinkConnector(KafkaConnector): """Kafka sink connector model.""" - connector_type: KafkaConnectorType = KafkaConnectorType.SINK + _connector_type: KafkaConnectorType = KafkaConnectorType.SINK @override def add_input_topics(self, topics: list[str]) -> None: diff --git a/kpops/components/base_components/kubernetes_app.py b/kpops/components/base_components/kubernetes_app.py index a6e1581cb..287cf0406 100644 --- a/kpops/components/base_components/kubernetes_app.py +++ b/kpops/components/base_components/kubernetes_app.py @@ -30,7 +30,7 @@ class KubernetesAppConfig(CamelCaseConfigModel, DescConfigModel): - """Settings specific to Kubernetes Apps""" + """Settings specific to Kubernetes Apps.""" model_config = ConfigDict( extra="allow", diff --git a/kpops/components/base_components/models/from_section.py b/kpops/components/base_components/models/from_section.py index 57e933f5d..bb82dc780 100644 --- a/kpops/components/base_components/models/from_section.py +++ b/kpops/components/base_components/models/from_section.py @@ -19,7 +19,7 @@ class InputTopicTypes(str, Enum): class FromTopic(DescConfigModel): - """Input topic + """Input topic. :param type: Topic type, defaults to None :param role: Custom identifier belonging to a topic; @@ -39,9 +39,10 @@ class FromTopic(DescConfigModel): @model_validator(mode="after") @classmethod def extra_topic_role(cls, values: Any) -> Any: - """Ensure that cls.role is used correctly, assign type if needed""" + """Ensure that cls.role is used correctly, assign type if needed.""" if values.type == InputTopicTypes.INPUT and values.role: - raise ValueError("Define role only if `type` is `pattern` or `None`") + msg = "Define role only if `type` is `pattern` or `None`" + raise ValueError(msg) return values @@ -49,7 +50,7 @@ def extra_topic_role(cls, values: Any) -> Any: class FromSection(DescConfigModel): - """Holds multiple input topics + """Holds multiple input topics. :param topics: Input topics :param components: Components to read from diff --git a/kpops/components/base_components/models/to_section.py b/kpops/components/base_components/models/to_section.py index 5bd0e9e03..743caf2a2 100644 --- a/kpops/components/base_components/models/to_section.py +++ b/kpops/components/base_components/models/to_section.py @@ -19,7 +19,7 @@ class OutputTopicTypes(str, Enum): class TopicConfig(DescConfigModel): - """Configure an output topic + """Configure an output topic. :param type: Topic type :param key_schema: Key schema class name @@ -66,14 +66,15 @@ class TopicConfig(DescConfigModel): @model_validator(mode="after") def extra_topic_role(cls, values: Any) -> Any: - """Ensure that cls.role is used correctly, assign type if needed""" + """Ensure that cls.role is used correctly, assign type if needed.""" if values.type and values.role: - raise ValueError("Define `role` only if `type` is undefined") + msg = "Define `role` only if `type` is undefined" + raise ValueError(msg) return values class ToSection(DescConfigModel): - """Holds multiple output topics + """Holds multiple output topics. :param topics: Output topics :param models: Data models diff --git a/kpops/components/base_components/pipeline_component.py b/kpops/components/base_components/pipeline_component.py index 8c8a7b292..7be212300 100644 --- a/kpops/components/base_components/pipeline_component.py +++ b/kpops/components/base_components/pipeline_component.py @@ -1,8 +1,9 @@ from __future__ import annotations -from pydantic import AliasChoices, ConfigDict, Field from abc import ABC +from pydantic import AliasChoices, ConfigDict, Field + from kpops.components.base_components.base_defaults_component import ( BaseDefaultsComponent, ) diff --git a/kpops/components/streams_bootstrap/streams/model.py b/kpops/components/streams_bootstrap/streams/model.py index bc5ced4e6..06c6bd8b2 100644 --- a/kpops/components/streams_bootstrap/streams/model.py +++ b/kpops/components/streams_bootstrap/streams/model.py @@ -1,4 +1,3 @@ -from collections.abc import Mapping, Set from typing import Any from pydantic import ConfigDict, Field, model_serializer @@ -72,23 +71,22 @@ def add_extra_input_topics(self, role: str, topics: list[str]) -> None: self.extra_input_topics.get(role, []) + topics ) - # @model_serializer(mode="wrap", when_used="always") - # def serialize_model(self, handler) -> dict[str, Any]: - # result = handler(self) - # # if dict(result.items()).get("extraInputTopics"): - # # breakpoint() - # extra_fields = set() - # if self.model_extra is not None: - # extra_fields = set(self.model_extra.keys()) - # fields = extra_fields.union(self.model_fields_set) - # filtered_result_extra_set = { - # k: v for k, v in result.items() if ((to_snake(k) in fields) or k in fields) - # } - # return filtered_result_extra_set + @model_serializer(mode="wrap", when_used="always") + def serialize_model(self, handler) -> dict[str, Any]: + result = handler(self) + # if dict(result.items()).get("extraInputTopics"): + # breakpoint() + extra_fields = set() + if self.model_extra is not None: + extra_fields = set(self.model_extra.keys()) + fields = extra_fields.union(self.model_fields_set) + return { + k: v for k, v in result.items() if ((to_snake(k) in fields) or k in fields) + } class StreamsAppAutoScaling(CamelCaseConfigModel, DescConfigModel): - """Kubernetes Event-driven Autoscaling config + """Kubernetes Event-driven Autoscaling config. :param enabled: Whether to enable auto-scaling using KEDA., defaults to False :param consumer_group: Name of the consumer group used for checking the diff --git a/kpops/utils/gen_schema.py b/kpops/utils/gen_schema.py index 6587d76ab..49ecdd4d5 100644 --- a/kpops/utils/gen_schema.py +++ b/kpops/utils/gen_schema.py @@ -1,14 +1,12 @@ -import json import inspect +import json import logging from abc import ABC -from collections.abc import Sequence from enum import Enum -from typing import Annotated, Any, Literal, Union +from typing import Literal -from pydantic import Field, TypeAdapter +from pydantic import Field from pydantic.json_schema import model_json_schema, models_json_schema -from pydantic_core import to_json from kpops.cli.pipeline_config import PipelineConfig from kpops.cli.registry import _find_classes @@ -47,7 +45,7 @@ def _add_components( components_module: str, components: tuple[type[PipelineComponent], ...] | None = None, ) -> tuple[type[PipelineComponent], ...]: - """Add components to a components tuple + """Add components to a components tuple. If an empty tuple is provided or it is not provided at all, the components types from the given module are 'tupled' @@ -84,14 +82,15 @@ def gen_pipeline_schema( log.warning("No components are provided, no schema is generated.") return # Add stock components if enabled - components: tuple[type[PipelineComponent], ...] = tuple() + components: tuple[type[PipelineComponent], ...] = () if include_stock_components: components = _add_components("kpops.components") # Add custom components if provided if components_module: components = _add_components(components_module, components) if not components: - raise RuntimeError("No valid components found.") + msg = "No valid components found." + raise RuntimeError(msg) # re-assign component type as Literal to work as discriminator for component in components: @@ -153,6 +152,6 @@ def gen_pipeline_schema( def gen_config_schema() -> None: - """Generate a json schema from the model of pipeline config""" + """Generate a json schema from the model of pipeline config.""" schema = model_json_schema(PipelineConfig) print(json.dumps(schema, indent=4, sort_keys=True)) diff --git a/tests/utils/test_doc_gen.py b/tests/utils/test_doc_gen.py index d234bd79d..184115e1b 100644 --- a/tests/utils/test_doc_gen.py +++ b/tests/utils/test_doc_gen.py @@ -22,7 +22,9 @@ def test_collect_fields(self): Ellipsis, Ellipsis, ] - actual = [field.field_info.default for field in collect_fields(ParentSettings)] + actual = [ + field_value.default for _, field_value in collect_fields(ParentSettings) + ] assert actual == expected @pytest.mark.parametrize(