[python] add typeddict models-mode for Python HTTP client emitter#10439
[python] add typeddict models-mode for Python HTTP client emitter#10439iscai-msft wants to merge 58 commits into
models-mode for Python HTTP client emitter#10439Conversation
Add a new 'typeddict' value for the models-mode option that generates Python TypedDict classes instead of DPG model classes. Key features: - TypedDict classes with Required[T]/NotRequired[T] annotations - TypedDict inheritance for non-discriminated models - Discriminated models: Union of leaf TypedDicts, no abstract base class - Input-only: operations accept TypedDict input, return dict output - Wire names used as TypedDict keys - _model_base.py still generated for serialization utilities Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
commit: |
|
All changed packages have been documented.
Show changes
|
|
You can try these changes here
|
…ypespec into python/addTypedDict
…hon/addTypedDict
- TypedDictModelType returns 'JSON' for response type annotations - Response.type_annotation/docstring passes is_response=True - Typeddict deserialization uses response.json() directly - Removed NotRequired from TypedDictModelSerializer (total=False handles it) - Updated mock API tests to verify JSON dict responses Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add client/naming typeddict variant to regenerate-common.ts - Create test_client_naming_typeddict.py with 11 tests verifying TypedDict uses wire names (defaultName, wireName) not client names - Tests cover: ClientNameModel, LanguageClientNameModel, ClientNameAndJsonEncodedNameModel, ClientModel, PythonModel Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
TypedDict is already JSON, so the MutableMapping[str, Any] overload is unnecessary. Only keep TypedDict model + IO[bytes] overloads. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Typeddict mode uses response.json() directly, so _deserialize is never called. Skip importing it to avoid W0611 unused-import lint warning. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove unused MutableMapping/Any imports from TypedDictModelType.imports() - Skip _deserialize import in paging_operation.py for typeddict mode Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ypespec into python/addTypedDict
TypedDictModelType returns 'JSON' for response type annotations but never defined the JSON = MutableMapping[str, Any] type alias, causing NameError at runtime. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
34e0cda to
95db199
Compare
…hon/addTypedDict
…of literals instead of enums when td only
- Fix cross-namespace parent imports in types.py to import from types module - Add isidentifier() check for wire names (not just iskeyword) for functional TypedDict form - Use __doc__ assignment for functional-form TypedDict docstrings - Add missing op_tools import in model_typeddict.py.jinja2 - Fix test docstring to match actual behavior - Update changelog entry with correct file names (types.py, _unions.py) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix cross-namespace parent imports in types_serializer.py and model_type.py to use get_relative_import_path with module_name parameter instead of naive string concatenation - Remove unused model_typeddict.py.jinja2 template (dead code) - Fix misleading test name: rename test_models_mode_dpg_no_typeddict_models to test_models_mode_dpg_typeddict_models_included Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| : property.serializationOptions?.json?.name) ?? property.name, | ||
| type: getType(context, sourceType), | ||
| optional: property.optional, | ||
| nullable: isNullable, |
There was a problem hiding this comment.
we are conflating nullable and optional. this is one step to help with the conflation, since it's extra important in typeddicts to not type things as optional when we mean nullable
Revert to the original no-sdk-clients check without tying the exemption to models-mode=typeddict. Models-only generation should not be gated on a specific models-mode. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| def type_annotation(self, **kwargs: Any) -> str: | ||
| if self.name: | ||
| return f'"_types.{self.name}"' | ||
| return f'"_unions.{self.name}"' |
There was a problem hiding this comment.
moved the existing union types to a unions.py file, keeping types.py purely for typeddict generation. So now, really instead of using JSON, we will be able to use the typeddict representations instead for those overloads. If we want to as well, we can not generate models and only generate typeddicts as well
| """ | ||
| if self.code_model.options["models-mode"]: | ||
| if self.is_typeddict_mode: | ||
| # In typeddict mode, enums are Literal aliases defined in types.py |
| self.cross_language_definition_id: Optional[str] = self.yaml_data.get("crossLanguageDefinitionId") | ||
| self.usage: int = self.yaml_data.get("usage", UsageFlags.Input.value | UsageFlags.Output.value) | ||
| self.client_namespace: str = self.yaml_data.get("clientNamespace", code_model.namespace) | ||
| self.is_typed_dict_only: bool = ( |
There was a problem hiding this comment.
we want to be able to flag individual models as typeddict only as well. we will need to add a decorator in tcgc in order to support this
Replace the JSON (MutableMapping) overload with a TypedDict reference from types.py for model input parameters. This gives users proper type-checking and IDE completion when passing dict literals. Changes: - preprocess: insert TypedDict model reference instead of any-object for both model and list/dict body params with DPG model elements - TypedDictModelType: add type_annotation, imports, docstring_type, and instance_check_template overrides - build_type: handle base='typeddict' to create TypedDictModelType - code_model: exclude typeddict refs from model_types (annotation-only) Generated operations now show: @overload def op(body: _models.X, ...) -> ... @overload def op(body: types.X, ...) -> ... @overload def op(body: IO[bytes], ...) -> ... JSON overloads are preserved for spread bodies (base=json) where no model class exists. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file is shared with typespec-python via pnpm sync and should not be deleted. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
I invoked workflow to generate code diff for this PR: Azure/azure-sdk-for-python#47346 for better review. |
When multiple namespaces import 'types', the second import shadows the
first causing mypy errors ('Name types already defined') and runtime
failures ('Name types.X is not defined').
Add get_unique_types_alias() (mirrors get_unique_models_alias) to
generate unique aliases like _types, _types_models1, _types_models2.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ypespec into python/addTypedDict
d6eb5ab to
4c0a3ac
Compare
In types.py (TypedDict definitions), use the JSON wire type instead of rich Python types for properties: - datetime.datetime → str - datetime.date → str - datetime.time → str - datetime.timedelta → str - datetime.datetime (unix) → int - decimal.Decimal → float - bytes → str This follows the principle that TypedDicts represent the wire/JSON shape of the data. Rich types like datetime are still used in model classes and operation signatures. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
fixes #8800
Add a new 'typeddict' value for the
models-modeoption that generates PythonTypedDictclasses instead of DPG model classes. Key features:_model_base.pystill generated for serialization utilities