diff --git a/.chronus/changes/python-discriminator-vartype-2026-10-7.md b/.chronus/changes/python-discriminator-vartype-2026-10-7.md new file mode 100644 index 00000000000..c5926d3851d --- /dev/null +++ b/.chronus/changes/python-discriminator-vartype-2026-10-7.md @@ -0,0 +1,7 @@ +--- +changeKind: fix +packages: + - "@typespec/http-client-python" +--- + +Fix the `:vartype` docstring of constant enum values (such as discriminator properties on polymorphic subclasses) to reference the enum class instead of a non-existent module-level symbol named after the enum member. diff --git a/packages/http-client-python/generator/pygen/codegen/models/enum_type.py b/packages/http-client-python/generator/pygen/codegen/models/enum_type.py index fd098b0923b..382822ceda5 100644 --- a/packages/http-client-python/generator/pygen/codegen/models/enum_type.py +++ b/packages/http-client-python/generator/pygen/codegen/models/enum_type.py @@ -59,7 +59,9 @@ def docstring_type(self, **kwargs: Any) -> str: client_namespace = self.enum_type.client_namespace if self.code_model.options.get("generation-subdir"): client_namespace += f".{self.code_model.options['generation-subdir']}" - enum_type_annotation = f"{client_namespace}.models.{self.name}" + # Reference the enum class, which is what the models module actually exports. + # The member name (e.g. ``MODEL_WEB_SUMMARIZATION``) is not a module-level symbol. + enum_type_annotation = f"{client_namespace}.models.{self.enum_type.name}" return f"{type_annotation} or ~{enum_type_annotation}" def get_json_template_representation( diff --git a/packages/http-client-python/tests/unit/test_typeddict.py b/packages/http-client-python/tests/unit/test_typeddict.py index 60e3114e810..8f53e30e659 100644 --- a/packages/http-client-python/tests/unit/test_typeddict.py +++ b/packages/http-client-python/tests/unit/test_typeddict.py @@ -21,6 +21,7 @@ _qualify_shadowed_builtins, ) from pygen.codegen.serializers.import_serializer import FileImportSerializer +from pygen.codegen.serializers.model_serializer import DpgModelSerializer from pygen.codegen.serializers.unions_serializer import UnionsSerializer @@ -756,6 +757,45 @@ def test_enum_value_dpg_imports_enums_module(): assert any("_enums" in module for module in modules) +def test_enum_value_docstring_type_references_enum_class(): + """The ``:vartype`` of a constant enum value (e.g. a discriminator) must reference the enum class. + + The enum member name (``RED``) is not a symbol of the models module, so ``~.models.RED`` + is an unresolvable Sphinx cross-reference; ``~.models.Color`` is the exported enum type. + """ + code_model = _make_code_model(models_mode="dpg") + enum_value = _make_enum_value(code_model) + namespace = enum_value.enum_type.client_namespace + assert enum_value.docstring_type() == f"str or ~{namespace}.models.Color" + assert enum_value.docstring_type() == enum_value.enum_type.docstring_type() + + +def test_enum_value_docstring_type_with_generation_subdir(): + """``generation-subdir`` is still honored when referencing the enum class.""" + code_model = _make_code_model(models_mode="dpg") + code_model.options["generation-subdir"] = "_generated" + enum_value = _make_enum_value(code_model) + namespace = enum_value.enum_type.client_namespace + assert enum_value.docstring_type() == f"str or ~{namespace}._generated.models.Color" + + +def test_discriminator_vartype_references_enum_class(): + """A discriminator subclass's ``:vartype`` points at the discriminator enum, not the member name.""" + code_model = _make_code_model(models_mode="dpg") + enum_value = _make_enum_value( + code_model, enum_name="ActivityRecordType", member_name="MODEL_WEB_SUMMARIZATION", value="modelWebSummarization" + ) + prop = Property( + yaml_data={"wireName": "type", "clientName": "type", "optional": False, "isDiscriminator": True}, + code_model=code_model, + type=enum_value, + ) + namespace = enum_value.enum_type.client_namespace + vartype = DpgModelSerializer.variable_documentation_string(prop)[-1] + assert vartype == f":vartype type: str or ~{namespace}.models.ActivityRecordType" + assert "MODEL_WEB_SUMMARIZATION" not in vartype + + # ---------- builtin-shadowing qualification (Literal-value false positives) ----------