From 029dec268fd6bf5e74e330ad90c252c5cd5b7027 Mon Sep 17 00:00:00 2001 From: jennypng <63012604+JennyPng@users.noreply.github.com> Date: Tue, 7 Jul 2026 15:11:45 -0700 Subject: [PATCH 1/6] version bumps --- .../http-client-python/eng/scripts/ci/dev_requirements.txt | 6 +++--- packages/http-client-python/tests/tox.ini | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/http-client-python/eng/scripts/ci/dev_requirements.txt b/packages/http-client-python/eng/scripts/ci/dev_requirements.txt index adfd8faac20..a52b260b058 100644 --- a/packages/http-client-python/eng/scripts/ci/dev_requirements.txt +++ b/packages/http-client-python/eng/scripts/ci/dev_requirements.txt @@ -1,8 +1,8 @@ -pyright==1.1.407 -pylint==4.0.4 +pyright==1.1.411 +pylint==4.0.6 tox==4.16.0 tox-uv -mypy==1.19.1 +mypy==2.1.0 colorama==0.4.6 debugpy==1.8.2 pytest==9.0.3 diff --git a/packages/http-client-python/tests/tox.ini b/packages/http-client-python/tests/tox.ini index a94a60603e4..44f8bb31b46 100644 --- a/packages/http-client-python/tests/tox.ini +++ b/packages/http-client-python/tests/tox.ini @@ -69,7 +69,7 @@ deps = -r {tox_root}/requirements/azure.txt -e {tox_root}/../generator commands = - uv pip install azure-pylint-guidelines-checker==0.5.2 --index-url="https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-python/pypi/simple/" + uv pip install azure-pylint-guidelines-checker==0.5.7 --index-url="https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-python/pypi/simple/" python {tox_root}/install_packages.py azure {tox_root} python {tox_root}/../eng/scripts/ci/run_pylint.py -t azure -s generated {posargs} From 4ffd9ae9f1cfbda58290f2231bd2c2d82665e829 Mon Sep 17 00:00:00 2001 From: jenny <63012604+JennyPng@users.noreply.github.com> Date: Tue, 7 Jul 2026 15:20:55 -0700 Subject: [PATCH 2/6] chronus --- .chronus/changes/vnext-bump-2026-6-7-22-13-32.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .chronus/changes/vnext-bump-2026-6-7-22-13-32.md diff --git a/.chronus/changes/vnext-bump-2026-6-7-22-13-32.md b/.chronus/changes/vnext-bump-2026-6-7-22-13-32.md new file mode 100644 index 00000000000..6822185e200 --- /dev/null +++ b/.chronus/changes/vnext-bump-2026-6-7-22-13-32.md @@ -0,0 +1,8 @@ +--- +# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking +changeKind: dependencies +packages: + - "@typespec/http-client-python" +--- + +[Python] Bump tool version targets: pylint 4.0.6, mypy 2.1.0, pyright 1.1.411 From 95f09731bfb6d31da0e7c0a450c872501c81ca00 Mon Sep 17 00:00:00 2001 From: jenny <63012604+JennyPng@users.noreply.github.com> Date: Mon, 13 Jul 2026 16:03:25 -0700 Subject: [PATCH 3/6] version bump --- packages/http-client-python/tests/tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/http-client-python/tests/tox.ini b/packages/http-client-python/tests/tox.ini index 44f8bb31b46..ecdc52886ba 100644 --- a/packages/http-client-python/tests/tox.ini +++ b/packages/http-client-python/tests/tox.ini @@ -69,7 +69,7 @@ deps = -r {tox_root}/requirements/azure.txt -e {tox_root}/../generator commands = - uv pip install azure-pylint-guidelines-checker==0.5.7 --index-url="https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-python/pypi/simple/" + uv pip install azure-pylint-guidelines-checker==0.5.8 --index-url="https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-python/pypi/simple/" python {tox_root}/install_packages.py azure {tox_root} python {tox_root}/../eng/scripts/ci/run_pylint.py -t azure -s generated {posargs} From 7619e99136653d06d5d42bbeda416a6b7428b1f5 Mon Sep 17 00:00:00 2001 From: jenny <63012604+JennyPng@users.noreply.github.com> Date: Mon, 13 Jul 2026 16:04:17 -0700 Subject: [PATCH 4/6] changelog --- .chronus/changes/vnext-bump-2026-6-7-22-13-32.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.chronus/changes/vnext-bump-2026-6-7-22-13-32.md b/.chronus/changes/vnext-bump-2026-6-7-22-13-32.md index 6822185e200..3abfe412b05 100644 --- a/.chronus/changes/vnext-bump-2026-6-7-22-13-32.md +++ b/.chronus/changes/vnext-bump-2026-6-7-22-13-32.md @@ -5,4 +5,4 @@ packages: - "@typespec/http-client-python" --- -[Python] Bump tool version targets: pylint 4.0.6, mypy 2.1.0, pyright 1.1.411 +[Python] Bump tool version targets: pylint 4.0.6, mypy 2.1.0, pyright 1.1.411, azure-pylint-guidelines-checker 0.5.8 From d4fcc824ef1d46af14677045486b82b7cbdb387e Mon Sep 17 00:00:00 2001 From: jennypng <63012604+JennyPng@users.noreply.github.com> Date: Tue, 14 Jul 2026 13:01:08 -0700 Subject: [PATCH 5/6] initial ci fixes, but need to fix pylint checker --- ...ring-guideline-checks-2026-7-14-11-30-0.md | 8 ++++++ .../eng/scripts/ci/config/pylintrc | 6 ++++- .../generator/pygen/codegen/models/client.py | 15 +++++++++++ .../pygen/codegen/models/operation_group.py | 2 ++ .../codegen/serializers/model_serializer.py | 25 ++++++++++++++++--- .../codegen/templates/model_base.py.jinja2 | 10 +++++++- .../codegen/templates/model_dpg.py.jinja2 | 5 ++++ .../codegen/templates/serialization.py.jinja2 | 6 ++++- 8 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 .chronus/changes/python-docstring-guideline-checks-2026-7-14-11-30-0.md diff --git a/.chronus/changes/python-docstring-guideline-checks-2026-7-14-11-30-0.md b/.chronus/changes/python-docstring-guideline-checks-2026-7-14-11-30-0.md new file mode 100644 index 00000000000..a2ca29b89fc --- /dev/null +++ b/.chronus/changes/python-docstring-guideline-checks-2026-7-14-11-30-0.md @@ -0,0 +1,8 @@ +--- +# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking +changeKind: fix +packages: + - "@typespec/http-client-python" +--- + +[Python] Generate model/client/config docstrings and targeted pylint suppressions that satisfy the updated `azure-pylint-guidelines-checker` docstring checks (`docstring-keyword-should-match-keyword-only`, `docstring-missing-param`) diff --git a/packages/http-client-python/eng/scripts/ci/config/pylintrc b/packages/http-client-python/eng/scripts/ci/config/pylintrc index 387021335fc..b6d3d4604cc 100644 --- a/packages/http-client-python/eng/scripts/ci/config/pylintrc +++ b/packages/http-client-python/eng/scripts/ci/config/pylintrc @@ -16,8 +16,12 @@ enable=useless-suppression # cyclic-import: because of https://github.com/PyCQA/pylint/issues/850 # too-many-arguments: Due to the nature of the CLI many commands have large arguments set which reflect in large arguments set in corresponding methods. # too-many-lines: Due to code generation many files end up with too many lines. +# no-cross-package-private-import: The guideline checker does not resolve relative +# imports (it ignores the import level), so legitimate same-package relative imports +# like `from ...operations._operations import ...` in generated async code are +# misreported as cross-package private imports. # Let's black deal with bad-continuation -disable=missing-docstring,locally-disabled,fixme,cyclic-import,too-many-arguments,invalid-name,duplicate-code,too-few-public-methods,consider-using-f-string,super-with-arguments,redefined-builtin,import-outside-toplevel,client-suffix-needed,unnecessary-dunder-call,unnecessary-ellipsis,disallowed-name,consider-using-max-builtin,unknown-option-value,file-needs-copyright-header,too-many-positional-arguments +disable=missing-docstring,locally-disabled,fixme,cyclic-import,too-many-arguments,invalid-name,duplicate-code,too-few-public-methods,consider-using-f-string,super-with-arguments,redefined-builtin,import-outside-toplevel,client-suffix-needed,unnecessary-dunder-call,unnecessary-ellipsis,disallowed-name,consider-using-max-builtin,unknown-option-value,file-needs-copyright-header,too-many-positional-arguments,no-cross-package-private-import [FORMAT] max-line-length=120 diff --git a/packages/http-client-python/generator/pygen/codegen/models/client.py b/packages/http-client-python/generator/pygen/codegen/models/client.py index abb4d83c49d..82bdcdf1508 100644 --- a/packages/http-client-python/generator/pygen/codegen/models/client.py +++ b/packages/http-client-python/generator/pygen/codegen/models/client.py @@ -53,6 +53,17 @@ def description(self) -> str: def name(self) -> str: return self.yaml_data["name"] + @property + def has_docstring_only_keyword(self) -> bool: + """Whether the constructor docstring documents a ``:keyword:`` that is not a real + keyword-only argument (it is popped from ``kwargs`` instead). + + This happens for params routed to ``**kwargs`` (e.g. ``api_version``): they are + still rendered as ``:keyword:`` in the docstring, so we must silence the + ``docstring-keyword-should-match-keyword-only`` guideline check. + """ + return any(p for p in self.parameters.method if p.method_location == ParameterMethodLocation.KWARG) + class Client(_ClientConfigBase[ClientGlobalParameterList]): """Model representing our service client""" @@ -187,6 +198,8 @@ def pylint_disable(self) -> str: retval = add_to_pylint_disable(retval, "too-many-instance-attributes") if len(self.name) > NAME_LENGTH_LIMIT: retval = add_to_pylint_disable(retval, "name-too-long") + if self.has_docstring_only_keyword or self.has_public_lro_operations: + retval = add_to_pylint_disable(retval, "docstring-keyword-should-match-keyword-only") return retval @property @@ -375,6 +388,8 @@ def pylint_disable(self) -> str: retval = add_to_pylint_disable("", "too-many-instance-attributes") if self.code_model.is_azure_flavor else "" if len(self.name) > NAME_LENGTH_LIMIT: retval = add_to_pylint_disable(retval, "name-too-long") + if self.has_docstring_only_keyword: + retval = add_to_pylint_disable(retval, "docstring-keyword-should-match-keyword-only") return retval @property diff --git a/packages/http-client-python/generator/pygen/codegen/models/operation_group.py b/packages/http-client-python/generator/pygen/codegen/models/operation_group.py index 5c0f467806c..798c7ae5b96 100644 --- a/packages/http-client-python/generator/pygen/codegen/models/operation_group.py +++ b/packages/http-client-python/generator/pygen/codegen/models/operation_group.py @@ -82,6 +82,8 @@ def pylint_disable(self) -> str: retval: str = "" if self.has_abstract_operations: retval = add_to_pylint_disable(retval, "abstract-class-instantiated") + if not self.is_mixin: + retval = add_to_pylint_disable(retval, "docstring-missing-param") if len(self.operations) > 20: retval = add_to_pylint_disable(retval, "too-many-public-methods") if len(self.class_name) > NAME_LENGTH_LIMIT and self.class_name[0] != "_": diff --git a/packages/http-client-python/generator/pygen/codegen/serializers/model_serializer.py b/packages/http-client-python/generator/pygen/codegen/serializers/model_serializer.py index 87a0b2cc4aa..15e82108a89 100644 --- a/packages/http-client-python/generator/pygen/codegen/serializers/model_serializer.py +++ b/packages/http-client-python/generator/pygen/codegen/serializers/model_serializer.py @@ -23,7 +23,7 @@ ) from .import_serializer import FileImportSerializer from .base_serializer import BaseSerializer -from ..models.utils import NamespaceType +from ..models.utils import NamespaceType, add_to_pylint_disable def _get_xml_deserializer_name(prop: Property) -> Optional[str]: # pylint: disable=too-many-return-statements @@ -122,6 +122,15 @@ def input_documentation_string(prop: Property) -> list[str]: # building the param line of the property doc return _documentation_string(prop, "keyword", "paramtype") + def keyword_documentation_string(self, model: ModelType) -> list[str]: + # building the ``:keyword:``/``:paramtype:`` lines for the keyword-only + # arguments of the generated ``__init__`` overload so that the docstring + # keeps a 1:1 correlation with the keyword-only arguments in the signature. + retval: list[str] = [] + for prop in sorted(self._init_line_parameters(model), key=lambda x: x.optional): + retval.extend(self.input_documentation_string(prop)) + return retval + @staticmethod def variable_documentation_string(prop: Property) -> list[str]: return _documentation_string(prop, "ivar", "vartype") @@ -199,6 +208,16 @@ def pylint_disable_items(self, model: ModelType) -> list[str]: def pylint_disable(self, model: ModelType) -> str: return " # pylint: disable=" + ", ".join(self.pylint_disable_items(model)) + def class_pylint_disable(self, model: ModelType) -> str: + """Class-level pylint disables for the model declaration line.""" + retval = model.pylint_disable() + # When the model's only constructor is ``def __init__(self, *args, **kwargs)`` (i.e. no + # typed overload is generated), the guideline checker reports the ``*args`` vararg as an + # undocumented param. There is no meaningful param to document, so silence the check. + if not self.need_init(model) and self.initialize_properties(model): + retval = add_to_pylint_disable(retval, "docstring-missing-param") + return retval + def global_pylint_disables(self) -> str: return "" @@ -244,7 +263,7 @@ def declare_model(self, model: ModelType) -> str: ) if model.parents: basename = ", ".join([m.name for m in model.parents]) - return f"class {model.name}({basename}):{model.pylint_disable()}" + return f"class {model.name}({basename}):{self.class_pylint_disable(model)}" @staticmethod def get_properties_to_initialize(model: ModelType) -> list[Property]: @@ -407,7 +426,7 @@ def declare_model(self, model: ModelType) -> str: basename = ", ".join([m.name for m in model.parents]) if model.discriminator_value: basename += f", discriminator='{model.discriminator_value}'" - return f"class {model.name}({basename}):{model.pylint_disable()}" + return f"class {model.name}({basename}):{self.class_pylint_disable(model)}" @staticmethod def get_properties_to_declare(model: ModelType) -> list[Property]: diff --git a/packages/http-client-python/generator/pygen/codegen/templates/model_base.py.jinja2 b/packages/http-client-python/generator/pygen/codegen/templates/model_base.py.jinja2 index e51a7645dde..40fde9fa098 100644 --- a/packages/http-client-python/generator/pygen/codegen/templates/model_base.py.jinja2 +++ b/packages/http-client-python/generator/pygen/codegen/templates/model_base.py.jinja2 @@ -161,7 +161,15 @@ def _is_readonly(p): class SdkJSONEncoder(JSONEncoder): - """A JSON encoder that's capable of serializing datetime objects and bytes.""" + """A JSON encoder that's capable of serializing datetime objects and bytes. + + :param args: Additional positional arguments passed to the base ``JSONEncoder``. + :type args: typing.Any + :keyword exclude_readonly: Whether to exclude readonly properties. Defaults to False. + :paramtype exclude_readonly: bool + :keyword format: The format to use for serialization. Defaults to None. + :paramtype format: typing.Optional[str] + """ def __init__(self, *args, exclude_readonly: bool = False, format: typing.Optional[str] = None, **kwargs): super().__init__(*args, **kwargs) diff --git a/packages/http-client-python/generator/pygen/codegen/templates/model_dpg.py.jinja2 b/packages/http-client-python/generator/pygen/codegen/templates/model_dpg.py.jinja2 index 8bcbbf4af41..6a2b533ae55 100644 --- a/packages/http-client-python/generator/pygen/codegen/templates/model_dpg.py.jinja2 +++ b/packages/http-client-python/generator/pygen/codegen/templates/model_dpg.py.jinja2 @@ -16,6 +16,11 @@ {% endfor %} {% endfor %} {% endif %} + {% if serializer.need_init(model) %} + {% for line in serializer.keyword_documentation_string(model) %} + {{ macros.wrap_model_string(line, '\n ') -}} + {% endfor %} + {% endif %} """ {% if model.is_polymorphic %} diff --git a/packages/http-client-python/generator/pygen/codegen/templates/serialization.py.jinja2 b/packages/http-client-python/generator/pygen/codegen/templates/serialization.py.jinja2 index 65a9c1f8f9e..8194e5c1566 100644 --- a/packages/http-client-python/generator/pygen/codegen/templates/serialization.py.jinja2 +++ b/packages/http-client-python/generator/pygen/codegen/templates/serialization.py.jinja2 @@ -476,7 +476,11 @@ def _decode_attribute_map_key(key): class Serializer: # pylint: disable=too-many-public-methods - """Request object model serializer.""" + """Request object model serializer. + + :param classes: Mapping of model names to model types, used to resolve models during serialization. + :type classes: typing.Optional[typing.Mapping[str, type]] + """ basic_types = {str: "str", int: "int", bool: "bool", float: "float"} From ecb0aa5bc3288f71b848f825940170bb23eb8957 Mon Sep 17 00:00:00 2001 From: jennypng <63012604+JennyPng@users.noreply.github.com> Date: Wed, 15 Jul 2026 10:16:56 -0700 Subject: [PATCH 6/6] 0.5.9 --- .chronus/changes/vnext-bump-2026-6-7-22-13-32.md | 2 +- packages/http-client-python/tests/tox.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.chronus/changes/vnext-bump-2026-6-7-22-13-32.md b/.chronus/changes/vnext-bump-2026-6-7-22-13-32.md index 3abfe412b05..de10b60169f 100644 --- a/.chronus/changes/vnext-bump-2026-6-7-22-13-32.md +++ b/.chronus/changes/vnext-bump-2026-6-7-22-13-32.md @@ -5,4 +5,4 @@ packages: - "@typespec/http-client-python" --- -[Python] Bump tool version targets: pylint 4.0.6, mypy 2.1.0, pyright 1.1.411, azure-pylint-guidelines-checker 0.5.8 +[Python] Bump tool version targets: pylint 4.0.6, mypy 2.1.0, pyright 1.1.411, azure-pylint-guidelines-checker 0.5.9 diff --git a/packages/http-client-python/tests/tox.ini b/packages/http-client-python/tests/tox.ini index ecdc52886ba..b661dd5afcd 100644 --- a/packages/http-client-python/tests/tox.ini +++ b/packages/http-client-python/tests/tox.ini @@ -69,7 +69,7 @@ deps = -r {tox_root}/requirements/azure.txt -e {tox_root}/../generator commands = - uv pip install azure-pylint-guidelines-checker==0.5.8 --index-url="https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-python/pypi/simple/" + uv pip install azure-pylint-guidelines-checker==0.5.9 --index-url="https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-python/pypi/simple/" python {tox_root}/install_packages.py azure {tox_root} python {tox_root}/../eng/scripts/ci/run_pylint.py -t azure -s generated {posargs}