Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: fix
packages:
- "@typespec/http-client-python"
---

Fix crash when generating with `models-mode=none`. Options passed to the `OptionsDict` constructor are now normalized through the same validation/transform path as `__setitem__`, so `models-mode=none` is correctly treated as falsy and a modelless client is produced instead of crashing.
5 changes: 4 additions & 1 deletion packages/http-client-python/generator/pygen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ class OptionsDict(MutableMapping):
}

def __init__(self, options: Optional[dict[str, Any]] = None) -> None:
self._data = options.copy() if options else {}
self._data = {}
if options:
for key, value in options.items():
self._data[key] = self._validate_and_transform(key, value)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Order-dependent validation risk: _validate_and_transform("package-mode", ...) reads self.get("from-typespec"). Since this loop transforms mid-construction, the result now depends on dict key order — if package-mode is processed before from-typespec, the latter reads as its default False, so an invalid --package-mode/--from-typespec combo can silently pass (or a valid one be wrongly rejected). Previously this branch only ran via __setitem__, after construction, so from-typespec was always populated.

Suggest populating raw values first, then transforming so cross-key lookups see full context:

Suggested change
self._data[key] = self._validate_and_transform(key, value)
self._data = options.copy() if options else {}
for key in list(self._data):
self._data[key] = self._validate_and_transform(key, self._data[key])

(models-mode="none" still normalizes to False, and re-validating False is a no-op.) A package-mode/from-typespec order regression test would be worth adding too.

self._validate_combinations()

def __getitem__(self, key: str) -> Any: # pylint: disable=too-many-return-statements
Expand Down
27 changes: 27 additions & 0 deletions packages/http-client-python/tests/unit/test_options_dict.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------

from pygen import OptionsDict


def test_models_mode_none_normalized_via_constructor():
# models-mode=none must be normalized to falsy False, even when passed
# through the constructor (not just __setitem__).
assert OptionsDict({"models-mode": "none"})["models-mode"] is False


def test_models_mode_none_normalized_via_setitem():
options = OptionsDict()
options["models-mode"] = "none"
assert options["models-mode"] is False


def test_constructor_and_setitem_agree():
via_ctor = OptionsDict({"models-mode": "none"})["models-mode"]
options = OptionsDict()
options["models-mode"] = "none"
via_setitem = options["models-mode"]
assert via_ctor == via_setitem
Loading