@typespec/http-client-python 0.33.0 → 0.34.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -3
- package/dist/emitter/emitter.d.ts.map +1 -1
- package/dist/emitter/emitter.js +2 -11
- package/dist/emitter/emitter.js.map +1 -1
- package/dist/emitter/external-process.d.ts +14 -0
- package/dist/emitter/external-process.d.ts.map +1 -1
- package/dist/emitter/external-process.js +17 -1
- package/dist/emitter/external-process.js.map +1 -1
- package/dist/emitter/lib.js +1 -1
- package/dist/emitter/lib.js.map +1 -1
- package/dist/emitter/node-runner.d.ts.map +1 -1
- package/dist/emitter/node-runner.js +2 -1
- package/dist/emitter/node-runner.js.map +1 -1
- package/dist/emitter/types.d.ts.map +1 -1
- package/dist/emitter/types.js +2 -0
- package/dist/emitter/types.js.map +1 -1
- package/dist/emitter/utils.d.ts +10 -0
- package/dist/emitter/utils.d.ts.map +1 -1
- package/dist/emitter/utils.js +12 -0
- package/dist/emitter/utils.js.map +1 -1
- package/emitter/src/emitter.ts +2 -14
- package/emitter/src/external-process.ts +18 -1
- package/emitter/src/lib.ts +1 -1
- package/emitter/src/node-runner.ts +2 -1
- package/emitter/src/types.ts +2 -0
- package/emitter/src/utils.ts +13 -0
- package/emitter/temp/tsconfig.tsbuildinfo +1 -1
- package/emitter/test/external-process.test.ts +28 -0
- package/emitter/test/utils.test.ts +12 -1
- package/eng/scripts/ci/regenerate-common.ts +40 -15
- package/eng/scripts/setup/__pycache__/package_manager.cpython-311.pyc +0 -0
- package/eng/scripts/setup/__pycache__/venvtools.cpython-311.pyc +0 -0
- package/generator/build/lib/pygen/__init__.py +3 -3
- package/generator/build/lib/pygen/codegen/models/__init__.py +4 -2
- package/generator/build/lib/pygen/codegen/models/code_model.py +27 -5
- package/generator/build/lib/pygen/codegen/models/combined_type.py +3 -3
- package/generator/build/lib/pygen/codegen/models/enum_type.py +75 -25
- package/generator/build/lib/pygen/codegen/models/list_type.py +10 -4
- package/generator/build/lib/pygen/codegen/models/model_type.py +149 -4
- package/generator/build/lib/pygen/codegen/models/operation.py +1 -0
- package/generator/build/lib/pygen/codegen/models/paging_operation.py +5 -1
- package/generator/build/lib/pygen/codegen/models/parameter.py +2 -2
- package/generator/build/lib/pygen/codegen/models/primitive_types.py +30 -6
- package/generator/build/lib/pygen/codegen/models/property.py +21 -8
- package/generator/build/lib/pygen/codegen/models/response.py +16 -3
- package/generator/build/lib/pygen/codegen/models/utils.py +3 -6
- package/generator/build/lib/pygen/codegen/serializers/__init__.py +46 -11
- package/generator/build/lib/pygen/codegen/serializers/builder_serializer.py +48 -15
- package/generator/build/lib/pygen/codegen/serializers/model_init_serializer.py +1 -1
- package/generator/build/lib/pygen/codegen/serializers/model_serializer.py +127 -3
- package/generator/build/lib/pygen/codegen/serializers/types_serializer.py +356 -12
- package/generator/build/lib/pygen/codegen/serializers/unions_serializer.py +44 -0
- package/generator/build/lib/pygen/codegen/templates/model_container.py.jinja2 +1 -1
- package/generator/build/lib/pygen/codegen/templates/types.py.jinja2 +51 -2
- package/generator/build/lib/pygen/codegen/templates/unions.py.jinja2 +9 -0
- package/generator/build/lib/pygen/preprocess/__init__.py +175 -162
- package/generator/build/lib/pygen/utils.py +6 -24
- package/generator/component-detection-pip-report.json +9 -9
- package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
- package/generator/pygen/__init__.py +3 -3
- package/generator/pygen/codegen/models/__init__.py +4 -2
- package/generator/pygen/codegen/models/code_model.py +27 -5
- package/generator/pygen/codegen/models/combined_type.py +3 -3
- package/generator/pygen/codegen/models/enum_type.py +75 -25
- package/generator/pygen/codegen/models/list_type.py +10 -4
- package/generator/pygen/codegen/models/model_type.py +149 -4
- package/generator/pygen/codegen/models/operation.py +1 -0
- package/generator/pygen/codegen/models/paging_operation.py +5 -1
- package/generator/pygen/codegen/models/parameter.py +2 -2
- package/generator/pygen/codegen/models/primitive_types.py +30 -6
- package/generator/pygen/codegen/models/property.py +21 -8
- package/generator/pygen/codegen/models/response.py +16 -3
- package/generator/pygen/codegen/models/utils.py +3 -6
- package/generator/pygen/codegen/serializers/__init__.py +46 -11
- package/generator/pygen/codegen/serializers/builder_serializer.py +48 -15
- package/generator/pygen/codegen/serializers/model_init_serializer.py +1 -1
- package/generator/pygen/codegen/serializers/model_serializer.py +127 -3
- package/generator/pygen/codegen/serializers/types_serializer.py +356 -12
- package/generator/pygen/codegen/serializers/unions_serializer.py +44 -0
- package/generator/pygen/codegen/templates/model_container.py.jinja2 +1 -1
- package/generator/pygen/codegen/templates/types.py.jinja2 +51 -2
- package/generator/pygen/codegen/templates/unions.py.jinja2 +9 -0
- package/generator/pygen/preprocess/__init__.py +175 -162
- package/generator/pygen/utils.py +6 -24
- package/generator/pygen.egg-info/SOURCES.txt +2 -0
- package/package.json +5 -5
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
"install": [
|
|
5
5
|
{
|
|
6
6
|
"download_info": {
|
|
7
|
-
"url": "https://files.pythonhosted.org/packages/
|
|
7
|
+
"url": "https://files.pythonhosted.org/packages/5d/40/e1e72872c6354b306daef1703549e8e83b4d43cfea356311bf722a043752/setuptools-83.0.0-py3-none-any.whl",
|
|
8
8
|
"archive_info": {
|
|
9
9
|
"hashes": {
|
|
10
|
-
"sha256": "
|
|
10
|
+
"sha256": "29b23c360f22f414dc7336bb39178cc7bcbf6021ed2733cde173f09dba19abb3"
|
|
11
11
|
},
|
|
12
|
-
"hash": "sha256=
|
|
12
|
+
"hash": "sha256=29b23c360f22f414dc7336bb39178cc7bcbf6021ed2733cde173f09dba19abb3"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"is_direct": false,
|
|
@@ -18,12 +18,12 @@
|
|
|
18
18
|
"metadata": {
|
|
19
19
|
"metadata_version": "2.4",
|
|
20
20
|
"name": "setuptools",
|
|
21
|
-
"version": "
|
|
21
|
+
"version": "83.0.0",
|
|
22
22
|
"dynamic": [
|
|
23
23
|
"license-file"
|
|
24
24
|
],
|
|
25
25
|
"summary": "Most extensible Python build backend with support for C/C++ extension modules",
|
|
26
|
-
"description": ".. |pypi-version| image:: https://img.shields.io/pypi/v/setuptools.svg\n :target: https://pypi.org/project/setuptools\n\n.. |py-version| image:: https://img.shields.io/pypi/pyversions/setuptools.svg\n\n.. |test-badge| image:: https://github.com/pypa/setuptools/actions/workflows/main.yml/badge.svg\n :target: https://github.com/pypa/setuptools/actions?query=workflow%3A%22tests%22\n :alt: tests\n\n.. |ruff-badge| image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json\n :target: https://github.com/astral-sh/ruff\n :alt: Ruff\n\n.. |docs-badge| image:: https://img.shields.io/readthedocs/setuptools/latest.svg\n :target: https://setuptools.pypa.io\n\n.. |skeleton-badge| image:: https://img.shields.io/badge/skeleton-
|
|
26
|
+
"description": ".. |pypi-version| image:: https://img.shields.io/pypi/v/setuptools.svg\n :target: https://pypi.org/project/setuptools\n\n.. |py-version| image:: https://img.shields.io/pypi/pyversions/setuptools.svg\n\n.. |test-badge| image:: https://github.com/pypa/setuptools/actions/workflows/main.yml/badge.svg\n :target: https://github.com/pypa/setuptools/actions?query=workflow%3A%22tests%22\n :alt: tests\n\n.. |ruff-badge| image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json\n :target: https://github.com/astral-sh/ruff\n :alt: Ruff\n\n.. |docs-badge| image:: https://img.shields.io/readthedocs/setuptools/latest.svg\n :target: https://setuptools.pypa.io\n\n.. |skeleton-badge| image:: https://img.shields.io/badge/skeleton-2026-informational\n :target: https://blog.jaraco.com/skeleton\n\n.. |codecov-badge| image:: https://img.shields.io/codecov/c/github/pypa/setuptools/master.svg?logo=codecov&logoColor=white\n :target: https://codecov.io/gh/pypa/setuptools\n\n.. |tidelift-badge| image:: https://tidelift.com/badges/github/pypa/setuptools?style=flat\n :target: https://tidelift.com/subscription/pkg/pypi-setuptools?utm_source=pypi-setuptools&utm_medium=readme\n\n.. |discord-badge| image:: https://img.shields.io/discord/803025117553754132\n :target: https://discord.com/channels/803025117553754132/815945031150993468\n :alt: Discord\n\n|pypi-version| |py-version| |test-badge| |ruff-badge| |docs-badge| |skeleton-badge| |codecov-badge| |discord-badge|\n\nSee the `Quickstart <https://setuptools.pypa.io/en/latest/userguide/quickstart.html>`_\nand the `User's Guide <https://setuptools.pypa.io/en/latest/userguide/>`_ for\ninstructions on how to use Setuptools.\n\nQuestions and comments should be directed to `GitHub Discussions\n<https://github.com/pypa/setuptools/discussions>`_.\nBug reports and especially tested patches may be\nsubmitted directly to the `bug tracker\n<https://github.com/pypa/setuptools/issues>`_.\n\n\nCode of Conduct\n===============\n\nEveryone interacting in the setuptools project's codebases, issue trackers,\nchat rooms, and fora is expected to follow the\n`PSF Code of Conduct <https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md>`_.\n\n\nFor Enterprise\n==============\n\nAvailable as part of the Tidelift Subscription.\n\nSetuptools and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use.\n\n`Learn more <https://tidelift.com/subscription/pkg/pypi-setuptools?utm_source=pypi-setuptools&utm_medium=referral&utm_campaign=github>`_.\n",
|
|
27
27
|
"description_content_type": "text/x-rst",
|
|
28
28
|
"keywords": [
|
|
29
29
|
"CPAN",
|
|
@@ -90,17 +90,17 @@
|
|
|
90
90
|
"wheel>=0.43.0; extra == \"core\"",
|
|
91
91
|
"jaraco.functools>=4; extra == \"core\"",
|
|
92
92
|
"more_itertools; extra == \"core\"",
|
|
93
|
-
"pytest-checkdocs>=2.
|
|
93
|
+
"pytest-checkdocs>=2.14; extra == \"check\"",
|
|
94
94
|
"pytest-ruff>=0.2.1; sys_platform != \"cygwin\" and extra == \"check\"",
|
|
95
95
|
"ruff>=0.13.0; sys_platform != \"cygwin\" and extra == \"check\"",
|
|
96
96
|
"pytest-cov; extra == \"cover\"",
|
|
97
|
-
"pytest-enabler>=
|
|
98
|
-
"pytest-mypy; extra == \"type\"",
|
|
97
|
+
"pytest-enabler>=3.4; extra == \"enabler\"",
|
|
98
|
+
"pytest-mypy>=1.0.1; platform_python_implementation != \"PyPy\" and extra == \"type\"",
|
|
99
99
|
"mypy==1.18.*; extra == \"type\"",
|
|
100
100
|
"importlib_metadata>=7.0.2; python_version < \"3.10\" and extra == \"type\"",
|
|
101
101
|
"jaraco.develop>=7.21; sys_platform != \"cygwin\" and extra == \"type\""
|
|
102
102
|
],
|
|
103
|
-
"requires_python": ">=3.
|
|
103
|
+
"requires_python": ">=3.10",
|
|
104
104
|
"project_url": [
|
|
105
105
|
"Source, https://github.com/pypa/setuptools",
|
|
106
106
|
"Documentation, https://setuptools.pypa.io/",
|
|
Binary file
|
|
@@ -168,10 +168,10 @@ class OptionsDict(MutableMapping):
|
|
|
168
168
|
if key == "models-mode" and value == "none":
|
|
169
169
|
value = False # switch to falsy value for easier code writing
|
|
170
170
|
|
|
171
|
-
if key == "models-mode" and value not in ["msrest", "dpg", False]:
|
|
171
|
+
if key == "models-mode" and value not in ["msrest", "dpg", "typeddict", False]:
|
|
172
172
|
raise ValueError(
|
|
173
|
-
"--models-mode can only be 'msrest', 'dpg' or 'none'. "
|
|
174
|
-
"Pass in 'msrest' if you want msrest models, or "
|
|
173
|
+
"--models-mode can only be 'msrest', 'dpg', 'typeddict', or 'none'. "
|
|
174
|
+
"Pass in 'msrest' if you want msrest models, 'typeddict' for TypedDict models, or "
|
|
175
175
|
"'none' if you don't want any."
|
|
176
176
|
)
|
|
177
177
|
if key == "package-mode":
|
|
@@ -9,7 +9,7 @@ from .base import BaseModel
|
|
|
9
9
|
from .base_builder import BaseBuilder, ParameterListType
|
|
10
10
|
from .code_model import CodeModel
|
|
11
11
|
from .client import Client
|
|
12
|
-
from .model_type import ModelType, JSONModelType, DPGModelType, MsrestModelType
|
|
12
|
+
from .model_type import ModelType, JSONModelType, DPGModelType, MsrestModelType, TypedDictModelType
|
|
13
13
|
from .dictionary_type import DictionaryType
|
|
14
14
|
from .list_type import ListType
|
|
15
15
|
from .combined_type import CombinedType
|
|
@@ -167,7 +167,9 @@ def build_type(yaml_data: dict[str, Any], code_model: CodeModel) -> BaseType:
|
|
|
167
167
|
response: Optional[BaseType] = None
|
|
168
168
|
if yaml_data["type"] == "model":
|
|
169
169
|
# need to special case model to avoid recursion
|
|
170
|
-
if yaml_data["base"] == "
|
|
170
|
+
if yaml_data["base"] == "typeddict":
|
|
171
|
+
model_type = TypedDictModelType # type: ignore
|
|
172
|
+
elif yaml_data["base"] == "json" or not code_model.options["models-mode"]:
|
|
171
173
|
model_type = JSONModelType
|
|
172
174
|
elif yaml_data["base"] == "dpg":
|
|
173
175
|
model_type = DPGModelType # type: ignore
|
|
@@ -152,16 +152,28 @@ class CodeModel: # pylint: disable=too-many-public-methods, disable=too-many-in
|
|
|
152
152
|
return result
|
|
153
153
|
return f"{result}{module_name}" if result.endswith(".") else f"{result}.{module_name}"
|
|
154
154
|
|
|
155
|
-
def
|
|
155
|
+
def _get_unique_import_alias(self, serialize_namespace: str, imported_namespace: str, module_name: str) -> str:
|
|
156
156
|
if not self.has_subnamespace:
|
|
157
|
-
return "
|
|
157
|
+
return f"_{module_name}"
|
|
158
158
|
relative_path = self.get_relative_import_path(
|
|
159
159
|
serialize_namespace, self.get_imported_namespace_for_model(imported_namespace)
|
|
160
160
|
)
|
|
161
161
|
dot_num = max(relative_path.count(".") - 1, 0)
|
|
162
|
-
|
|
162
|
+
path_parts = [p for p in relative_path.split(".") if p]
|
|
163
|
+
# For "models", keep existing format: _<path_parts><dot_num> (e.g. _models1, _firstnamespace_models2)
|
|
164
|
+
# For other modules like "types", prefix with module name: _types_<path_parts><dot_num>
|
|
165
|
+
if module_name == "models":
|
|
166
|
+
parts = [""] + (path_parts or [module_name])
|
|
167
|
+
else:
|
|
168
|
+
parts = [f"_{module_name}"] + (path_parts or [])
|
|
163
169
|
return "_".join(parts) + (str(dot_num) if dot_num > 0 else "")
|
|
164
170
|
|
|
171
|
+
def get_unique_models_alias(self, serialize_namespace: str, imported_namespace: str) -> str:
|
|
172
|
+
return self._get_unique_import_alias(serialize_namespace, imported_namespace, "models")
|
|
173
|
+
|
|
174
|
+
def get_unique_types_alias(self, serialize_namespace: str, imported_namespace: str) -> str:
|
|
175
|
+
return self._get_unique_import_alias(serialize_namespace, imported_namespace, "types")
|
|
176
|
+
|
|
165
177
|
@property
|
|
166
178
|
def client_namespace_types(self) -> dict[str, ClientNamespaceType]:
|
|
167
179
|
if not self._client_namespace_types:
|
|
@@ -174,6 +186,14 @@ class CodeModel: # pylint: disable=too-many-public-methods, disable=too-many-in
|
|
|
174
186
|
if model.client_namespace not in self._client_namespace_types:
|
|
175
187
|
self._client_namespace_types[model.client_namespace] = ClientNamespaceType()
|
|
176
188
|
self._client_namespace_types[model.client_namespace].models.append(model)
|
|
189
|
+
# TypedDict copies (base="typeddict") are excluded from model_types to keep
|
|
190
|
+
# them out of _models.py, but they need to be in the namespace model list
|
|
191
|
+
# so the TypesSerializer can render them in types.py.
|
|
192
|
+
for t in self.types_map.values():
|
|
193
|
+
if isinstance(t, ModelType) and t.base == "typeddict" and t.usage != UsageFlags.Default.value:
|
|
194
|
+
if t.client_namespace not in self._client_namespace_types:
|
|
195
|
+
self._client_namespace_types[t.client_namespace] = ClientNamespaceType()
|
|
196
|
+
self._client_namespace_types[t.client_namespace].models.append(t)
|
|
177
197
|
for enum in self.enums:
|
|
178
198
|
if enum.client_namespace not in self._client_namespace_types:
|
|
179
199
|
self._client_namespace_types[enum.client_namespace] = ClientNamespaceType()
|
|
@@ -339,7 +359,9 @@ class CodeModel: # pylint: disable=too-many-public-methods, disable=too-many-in
|
|
|
339
359
|
"""All of the model types in this class"""
|
|
340
360
|
if not self._model_types:
|
|
341
361
|
self._model_types = [
|
|
342
|
-
t
|
|
362
|
+
t
|
|
363
|
+
for t in self.types_map.values()
|
|
364
|
+
if isinstance(t, ModelType) and t.usage != UsageFlags.Default.value and t.base != "typeddict"
|
|
343
365
|
]
|
|
344
366
|
return self._model_types
|
|
345
367
|
|
|
@@ -349,7 +371,7 @@ class CodeModel: # pylint: disable=too-many-public-methods, disable=too-many-in
|
|
|
349
371
|
|
|
350
372
|
@staticmethod
|
|
351
373
|
def get_public_model_types(models: list[ModelType]) -> list[ModelType]:
|
|
352
|
-
return [m for m in models if not m.internal and not m.base == "json"]
|
|
374
|
+
return [m for m in models if not m.internal and not m.base == "json" and not m.is_typed_dict_only]
|
|
353
375
|
|
|
354
376
|
@property
|
|
355
377
|
def public_model_types(self) -> list[ModelType]:
|
|
@@ -66,7 +66,7 @@ class CombinedType(BaseType):
|
|
|
66
66
|
|
|
67
67
|
def type_annotation(self, **kwargs: Any) -> str:
|
|
68
68
|
if self.name:
|
|
69
|
-
return f'"
|
|
69
|
+
return f'"_unions.{self.name}"'
|
|
70
70
|
return self.type_definition(**kwargs)
|
|
71
71
|
|
|
72
72
|
def type_definition(self, **kwargs: Any) -> str:
|
|
@@ -116,10 +116,10 @@ class CombinedType(BaseType):
|
|
|
116
116
|
file_import = FileImport(self.code_model)
|
|
117
117
|
serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace)
|
|
118
118
|
serialize_namespace_type = kwargs.get("serialize_namespace_type")
|
|
119
|
-
if self.name and serialize_namespace_type != NamespaceType.
|
|
119
|
+
if self.name and serialize_namespace_type != NamespaceType.UNIONS_FILE:
|
|
120
120
|
file_import.add_submodule_import(
|
|
121
121
|
self.code_model.get_relative_import_path(serialize_namespace),
|
|
122
|
-
"
|
|
122
|
+
"_unions",
|
|
123
123
|
ImportType.LOCAL,
|
|
124
124
|
TypingSection.TYPING,
|
|
125
125
|
)
|
|
@@ -167,6 +167,10 @@ class EnumType(BaseType):
|
|
|
167
167
|
enum_description = f"Known values are: {possible_values_str}."
|
|
168
168
|
return enum_description
|
|
169
169
|
|
|
170
|
+
@property
|
|
171
|
+
def is_typeddict_mode(self) -> bool:
|
|
172
|
+
return self.code_model.options["models-mode"] == "typeddict"
|
|
173
|
+
|
|
170
174
|
def type_annotation(self, **kwargs: Any) -> str:
|
|
171
175
|
"""The python type used for type annotation
|
|
172
176
|
|
|
@@ -174,9 +178,20 @@ class EnumType(BaseType):
|
|
|
174
178
|
:rtype: str
|
|
175
179
|
"""
|
|
176
180
|
if self.code_model.options["models-mode"]:
|
|
181
|
+
if self.is_typeddict_mode:
|
|
182
|
+
# In typeddict mode, enums are Literal aliases defined in types.py
|
|
183
|
+
serialize_namespace_type = kwargs.get("serialize_namespace_type")
|
|
184
|
+
if serialize_namespace_type == NamespaceType.TYPES_FILE:
|
|
185
|
+
# Same file — just the name, no module prefix
|
|
186
|
+
return self.name
|
|
187
|
+
# From operation/client files, use types.EnumName (matching model pattern)
|
|
188
|
+
return f"types.{self.name}"
|
|
177
189
|
|
|
178
190
|
module_name = ""
|
|
179
|
-
|
|
191
|
+
serialize_namespace_type = kwargs.get("serialize_namespace_type")
|
|
192
|
+
if serialize_namespace_type == NamespaceType.TYPES_FILE:
|
|
193
|
+
pass # no module prefix for types.py
|
|
194
|
+
elif kwargs.get("need_model_alias", True):
|
|
180
195
|
serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace)
|
|
181
196
|
model_alias = self.code_model.get_unique_models_alias(serialize_namespace, self.client_namespace)
|
|
182
197
|
module_name = f"{model_alias}."
|
|
@@ -240,31 +255,66 @@ class EnumType(BaseType):
|
|
|
240
255
|
file_import = FileImport(self.code_model)
|
|
241
256
|
file_import.merge(self.value_type.imports(**kwargs))
|
|
242
257
|
if self.code_model.options["models-mode"]:
|
|
243
|
-
|
|
258
|
+
if self.is_typeddict_mode:
|
|
259
|
+
# In typeddict mode, enums are Literal aliases in types.py — no Union needed
|
|
260
|
+
serialize_namespace_type = kwargs.get("serialize_namespace_type")
|
|
261
|
+
if serialize_namespace_type == NamespaceType.TYPES_FILE:
|
|
262
|
+
# Same file — no import needed for same-namespace enums
|
|
263
|
+
serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace)
|
|
264
|
+
if self.client_namespace != serialize_namespace:
|
|
265
|
+
# Cross-namespace: import from sibling types module
|
|
266
|
+
relative_path = self.code_model.get_relative_import_path(
|
|
267
|
+
serialize_namespace, self.client_namespace
|
|
268
|
+
)
|
|
269
|
+
file_import.add_submodule_import(
|
|
270
|
+
f"{relative_path}types" if relative_path != "." else ".types",
|
|
271
|
+
self.name,
|
|
272
|
+
ImportType.LOCAL,
|
|
273
|
+
typing_section=TypingSection.REGULAR,
|
|
274
|
+
)
|
|
275
|
+
elif serialize_namespace_type in [NamespaceType.OPERATION, NamespaceType.CLIENT]:
|
|
276
|
+
# Import types module directly (matching model pattern)
|
|
277
|
+
serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace)
|
|
278
|
+
relative_path = self.code_model.get_relative_import_path(serialize_namespace, self.client_namespace)
|
|
279
|
+
file_import.add_submodule_import(
|
|
280
|
+
relative_path,
|
|
281
|
+
"types",
|
|
282
|
+
ImportType.LOCAL,
|
|
283
|
+
)
|
|
284
|
+
else:
|
|
285
|
+
file_import.add_submodule_import("typing", "Union", ImportType.STDLIB, TypingSection.REGULAR)
|
|
244
286
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
287
|
+
serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace)
|
|
288
|
+
relative_path = self.code_model.get_relative_import_path(serialize_namespace, self.client_namespace)
|
|
289
|
+
alias = self.code_model.get_unique_models_alias(serialize_namespace, self.client_namespace)
|
|
290
|
+
serialize_namespace_type = kwargs.get("serialize_namespace_type")
|
|
291
|
+
called_by_property = kwargs.get("called_by_property", False)
|
|
292
|
+
if serialize_namespace_type in [NamespaceType.OPERATION, NamespaceType.CLIENT]:
|
|
293
|
+
file_import.add_submodule_import(
|
|
294
|
+
relative_path,
|
|
295
|
+
"models",
|
|
296
|
+
ImportType.LOCAL,
|
|
297
|
+
alias=alias,
|
|
298
|
+
typing_section=TypingSection.REGULAR,
|
|
299
|
+
)
|
|
300
|
+
elif serialize_namespace_type == NamespaceType.TYPES_FILE:
|
|
301
|
+
# Import enum name directly to avoid dotted forward refs in TypedDict annotations
|
|
302
|
+
file_import.add_submodule_import(
|
|
303
|
+
f"{relative_path}models" if relative_path != "." else ".models",
|
|
304
|
+
self.name,
|
|
305
|
+
ImportType.LOCAL,
|
|
306
|
+
typing_section=TypingSection.TYPING,
|
|
307
|
+
)
|
|
308
|
+
elif serialize_namespace_type == NamespaceType.UNIONS_FILE or (
|
|
309
|
+
serialize_namespace_type == NamespaceType.MODEL and called_by_property
|
|
310
|
+
):
|
|
311
|
+
file_import.add_submodule_import(
|
|
312
|
+
relative_path,
|
|
313
|
+
"models",
|
|
314
|
+
ImportType.LOCAL,
|
|
315
|
+
alias=alias,
|
|
316
|
+
typing_section=TypingSection.TYPING,
|
|
317
|
+
)
|
|
268
318
|
|
|
269
319
|
file_import.merge(self.value_type.imports(**kwargs))
|
|
270
320
|
return file_import
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
from typing import Any, Optional, Union, TYPE_CHECKING
|
|
7
7
|
from .base import BaseType
|
|
8
8
|
from .imports import FileImport
|
|
9
|
+
from .utils import NamespaceType
|
|
9
10
|
|
|
10
11
|
if TYPE_CHECKING:
|
|
11
12
|
from .code_model import CodeModel
|
|
@@ -41,12 +42,17 @@ class ListType(BaseType):
|
|
|
41
42
|
# this means we're version tolerant XML, we just return the XML element
|
|
42
43
|
return self.element_type.type_annotation(**kwargs)
|
|
43
44
|
|
|
44
|
-
# if there is a function
|
|
45
|
+
# if there is a function named `list` we have to make sure there's no conflict with the built-in `list`
|
|
46
|
+
# in operation files. The operation_groups_serializer defines `List = list` alias for this case.
|
|
47
|
+
serialize_namespace_type = kwargs.get("serialize_namespace_type")
|
|
45
48
|
is_operation_file = kwargs.get("is_operation_file", False)
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
in_operation_context = (
|
|
50
|
+
serialize_namespace_type in (NamespaceType.OPERATION, NamespaceType.CLIENT) or is_operation_file
|
|
48
51
|
)
|
|
49
|
-
|
|
52
|
+
if in_operation_context and self.code_model.has_operation_named_list:
|
|
53
|
+
list_type = "List"
|
|
54
|
+
else:
|
|
55
|
+
list_type = "list"
|
|
50
56
|
return f"{list_type}[{self.element_type.type_annotation(**kwargs)}]"
|
|
51
57
|
|
|
52
58
|
def description(self, *, is_operation_file: bool) -> str:
|
|
@@ -77,11 +77,23 @@ class ModelType(BaseType): # pylint: disable=too-many-instance-attributes, too-
|
|
|
77
77
|
self.cross_language_definition_id: Optional[str] = self.yaml_data.get("crossLanguageDefinitionId")
|
|
78
78
|
self.usage: int = self.yaml_data.get("usage", UsageFlags.Input.value | UsageFlags.Output.value)
|
|
79
79
|
self.client_namespace: str = self.yaml_data.get("clientNamespace", code_model.namespace)
|
|
80
|
+
self.is_typed_dict_only: bool = (
|
|
81
|
+
self.yaml_data.get("typedDictOnly", False) or code_model.options["models-mode"] == "typeddict"
|
|
82
|
+
)
|
|
80
83
|
|
|
81
84
|
@property
|
|
82
85
|
def is_usage_output(self) -> bool:
|
|
83
86
|
return bool(self.usage & UsageFlags.Output.value)
|
|
84
87
|
|
|
88
|
+
@property
|
|
89
|
+
def is_usage_input(self) -> bool:
|
|
90
|
+
return bool(self.usage & UsageFlags.Input.value)
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def is_used_in_operations_via_types(self) -> bool:
|
|
94
|
+
"""Whether this model would be imported from types.py (not models) in operations."""
|
|
95
|
+
return False
|
|
96
|
+
|
|
85
97
|
@property
|
|
86
98
|
def flattened_property(self) -> Optional[Property]:
|
|
87
99
|
try:
|
|
@@ -275,12 +287,20 @@ class GeneratedModelType(ModelType):
|
|
|
275
287
|
def type_annotation(self, **kwargs: Any) -> str:
|
|
276
288
|
is_operation_file = kwargs.pop("is_operation_file", False)
|
|
277
289
|
skip_quote = kwargs.get("skip_quote", False)
|
|
290
|
+
serialize_namespace_type = kwargs.get("serialize_namespace_type")
|
|
278
291
|
module_name = ""
|
|
279
|
-
|
|
292
|
+
# In types.py, use bare name to avoid pyright "variable in type expression" errors
|
|
293
|
+
if serialize_namespace_type == NamespaceType.TYPES_FILE:
|
|
294
|
+
pass # no module prefix, no internal file prefix
|
|
295
|
+
elif kwargs.get("need_model_alias", True):
|
|
280
296
|
serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace)
|
|
281
297
|
model_alias = self.code_model.get_unique_models_alias(serialize_namespace, self.client_namespace)
|
|
282
298
|
module_name = f"{model_alias}."
|
|
283
|
-
file_name =
|
|
299
|
+
file_name = (
|
|
300
|
+
f"{self.code_model.models_filename}."
|
|
301
|
+
if self.internal and serialize_namespace_type != NamespaceType.TYPES_FILE
|
|
302
|
+
else ""
|
|
303
|
+
)
|
|
284
304
|
retval = module_name + file_name + self.name
|
|
285
305
|
return retval if is_operation_file or skip_quote else f'"{retval}"'
|
|
286
306
|
|
|
@@ -305,7 +325,7 @@ class GeneratedModelType(ModelType):
|
|
|
305
325
|
alias = self.code_model.get_unique_models_alias(serialize_namespace, self.client_namespace)
|
|
306
326
|
serialize_namespace_type = kwargs.get("serialize_namespace_type")
|
|
307
327
|
called_by_property = kwargs.get("called_by_property", False)
|
|
308
|
-
# add import for models in operations or
|
|
328
|
+
# add import for models in operations, types, or unions file
|
|
309
329
|
if serialize_namespace_type in [NamespaceType.OPERATION, NamespaceType.CLIENT]:
|
|
310
330
|
file_import.add_submodule_import(
|
|
311
331
|
relative_path,
|
|
@@ -320,7 +340,33 @@ class GeneratedModelType(ModelType):
|
|
|
320
340
|
ImportType.LOCAL,
|
|
321
341
|
alias="_Model",
|
|
322
342
|
)
|
|
323
|
-
elif serialize_namespace_type == NamespaceType.TYPES_FILE
|
|
343
|
+
elif serialize_namespace_type == NamespaceType.TYPES_FILE:
|
|
344
|
+
# Don't import models that will be defined in this namespace's types.py —
|
|
345
|
+
# either as TypedDict classes (non-discriminated) or as Union aliases (discriminated bases).
|
|
346
|
+
# Only same-namespace non-json models are in the same types.py file.
|
|
347
|
+
same_namespace = relative_path == "."
|
|
348
|
+
will_be_in_types_file = self.base != "json" and same_namespace
|
|
349
|
+
if not will_be_in_types_file:
|
|
350
|
+
if same_namespace:
|
|
351
|
+
# json models from same namespace — import from .models (or .models._models for internal)
|
|
352
|
+
import_path = f".models.{self.code_model.models_filename}" if self.internal else ".models"
|
|
353
|
+
file_import.add_submodule_import(
|
|
354
|
+
import_path,
|
|
355
|
+
self.name,
|
|
356
|
+
ImportType.LOCAL,
|
|
357
|
+
typing_section=TypingSection.TYPING,
|
|
358
|
+
)
|
|
359
|
+
else:
|
|
360
|
+
# Cross-namespace model — import from sibling namespace's types module
|
|
361
|
+
file_import.add_submodule_import(
|
|
362
|
+
self.code_model.get_relative_import_path(
|
|
363
|
+
serialize_namespace, self.client_namespace, module_name="types"
|
|
364
|
+
),
|
|
365
|
+
self.name,
|
|
366
|
+
ImportType.LOCAL,
|
|
367
|
+
typing_section=TypingSection.TYPING,
|
|
368
|
+
)
|
|
369
|
+
elif serialize_namespace_type == NamespaceType.UNIONS_FILE or (
|
|
324
370
|
serialize_namespace_type == NamespaceType.MODEL and called_by_property
|
|
325
371
|
):
|
|
326
372
|
file_import.add_submodule_import(
|
|
@@ -352,6 +398,33 @@ class MsrestModelType(GeneratedModelType):
|
|
|
352
398
|
class DPGModelType(GeneratedModelType):
|
|
353
399
|
base = "dpg"
|
|
354
400
|
|
|
401
|
+
@property
|
|
402
|
+
def is_used_in_operations_via_types(self) -> bool:
|
|
403
|
+
return self.is_typed_dict_only
|
|
404
|
+
|
|
405
|
+
def type_annotation(self, **kwargs: Any) -> str:
|
|
406
|
+
if self.is_typed_dict_only:
|
|
407
|
+
is_operation_file = kwargs.pop("is_operation_file", False)
|
|
408
|
+
skip_quote = kwargs.get("skip_quote", False)
|
|
409
|
+
serialize_namespace_type = kwargs.get("serialize_namespace_type")
|
|
410
|
+
# Within types.py, use bare name (no module prefix)
|
|
411
|
+
if serialize_namespace_type == NamespaceType.TYPES_FILE:
|
|
412
|
+
retval = self.name
|
|
413
|
+
else:
|
|
414
|
+
serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace)
|
|
415
|
+
types_alias = self.code_model.get_unique_types_alias(serialize_namespace, self.client_namespace)
|
|
416
|
+
retval = f"{types_alias}.{self.name}"
|
|
417
|
+
return retval if is_operation_file or skip_quote else f'"{retval}"'
|
|
418
|
+
return super().type_annotation(**kwargs)
|
|
419
|
+
|
|
420
|
+
def docstring_type(self, **kwargs: Any) -> str:
|
|
421
|
+
if self.is_typed_dict_only:
|
|
422
|
+
client_namespace = self.client_namespace
|
|
423
|
+
if self.code_model.options.get("generation-subdir"):
|
|
424
|
+
client_namespace += f".{self.code_model.options['generation-subdir']}"
|
|
425
|
+
return f"~{client_namespace}.types.{self.name}"
|
|
426
|
+
return super().docstring_type(**kwargs)
|
|
427
|
+
|
|
355
428
|
def serialization_type(self, **kwargs: Any) -> str:
|
|
356
429
|
return (
|
|
357
430
|
self.type_annotation(skip_quote=True, **kwargs)
|
|
@@ -364,7 +437,79 @@ class DPGModelType(GeneratedModelType):
|
|
|
364
437
|
return "isinstance({}, " + f"_models.{self.name})"
|
|
365
438
|
|
|
366
439
|
def imports(self, **kwargs: Any) -> FileImport:
|
|
440
|
+
if self.is_typed_dict_only:
|
|
441
|
+
file_import = FileImport(self.code_model)
|
|
442
|
+
serialize_namespace_type = kwargs.get("serialize_namespace_type")
|
|
443
|
+
serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace)
|
|
444
|
+
relative_path = self.code_model.get_relative_import_path(serialize_namespace, self.client_namespace)
|
|
445
|
+
alias = self.code_model.get_unique_types_alias(serialize_namespace, self.client_namespace)
|
|
446
|
+
same_namespace = relative_path == "."
|
|
447
|
+
if serialize_namespace_type in [NamespaceType.OPERATION, NamespaceType.CLIENT]:
|
|
448
|
+
file_import.add_submodule_import(
|
|
449
|
+
relative_path,
|
|
450
|
+
"types",
|
|
451
|
+
ImportType.LOCAL,
|
|
452
|
+
alias=alias,
|
|
453
|
+
)
|
|
454
|
+
elif serialize_namespace_type == NamespaceType.TYPES_FILE and same_namespace:
|
|
455
|
+
pass # model is defined in this types.py — no import needed
|
|
456
|
+
elif serialize_namespace_type in [NamespaceType.TYPES_FILE, NamespaceType.UNIONS_FILE] or (
|
|
457
|
+
serialize_namespace_type == NamespaceType.MODEL and kwargs.get("called_by_property", False)
|
|
458
|
+
):
|
|
459
|
+
file_import.add_submodule_import(
|
|
460
|
+
relative_path,
|
|
461
|
+
"types",
|
|
462
|
+
ImportType.LOCAL,
|
|
463
|
+
alias=alias,
|
|
464
|
+
typing_section=TypingSection.TYPING,
|
|
465
|
+
)
|
|
466
|
+
return file_import
|
|
367
467
|
file_import = super().imports(**kwargs)
|
|
368
468
|
if self.flattened_property:
|
|
369
469
|
file_import.add_submodule_import("typing", "Any", ImportType.STDLIB)
|
|
370
470
|
return file_import
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
class TypedDictModelType(DPGModelType):
|
|
474
|
+
base = "typeddict"
|
|
475
|
+
|
|
476
|
+
@property
|
|
477
|
+
def is_used_in_operations_via_types(self) -> bool:
|
|
478
|
+
return True
|
|
479
|
+
|
|
480
|
+
def type_annotation(self, **kwargs: Any) -> str:
|
|
481
|
+
is_operation_file = kwargs.pop("is_operation_file", False)
|
|
482
|
+
skip_quote = kwargs.get("skip_quote", False)
|
|
483
|
+
serialize_namespace_type = kwargs.get("serialize_namespace_type")
|
|
484
|
+
if serialize_namespace_type == NamespaceType.TYPES_FILE:
|
|
485
|
+
retval = self.name
|
|
486
|
+
else:
|
|
487
|
+
serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace)
|
|
488
|
+
types_alias = self.code_model.get_unique_types_alias(serialize_namespace, self.client_namespace)
|
|
489
|
+
retval = f"{types_alias}.{self.name}"
|
|
490
|
+
return retval if is_operation_file or skip_quote else f'"{retval}"'
|
|
491
|
+
|
|
492
|
+
def docstring_type(self, **kwargs: Any) -> str:
|
|
493
|
+
client_namespace = self.client_namespace
|
|
494
|
+
if self.code_model.options.get("generation-subdir"):
|
|
495
|
+
client_namespace += f".{self.code_model.options['generation-subdir']}"
|
|
496
|
+
return f"~{client_namespace}.types.{self.name}"
|
|
497
|
+
|
|
498
|
+
@property
|
|
499
|
+
def instance_check_template(self) -> str:
|
|
500
|
+
return "isinstance({}, MutableMapping)"
|
|
501
|
+
|
|
502
|
+
def imports(self, **kwargs: Any) -> FileImport:
|
|
503
|
+
file_import = FileImport(self.code_model)
|
|
504
|
+
serialize_namespace_type = kwargs.get("serialize_namespace_type")
|
|
505
|
+
serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace)
|
|
506
|
+
relative_path = self.code_model.get_relative_import_path(serialize_namespace, self.client_namespace)
|
|
507
|
+
alias = self.code_model.get_unique_types_alias(serialize_namespace, self.client_namespace)
|
|
508
|
+
if serialize_namespace_type in [NamespaceType.OPERATION, NamespaceType.CLIENT]:
|
|
509
|
+
file_import.add_submodule_import(
|
|
510
|
+
relative_path,
|
|
511
|
+
"types",
|
|
512
|
+
ImportType.LOCAL,
|
|
513
|
+
alias=alias,
|
|
514
|
+
)
|
|
515
|
+
return file_import
|
|
@@ -458,6 +458,7 @@ class OperationBase( # pylint: disable=too-many-public-methods,too-many-instanc
|
|
|
458
458
|
r.type
|
|
459
459
|
and not isinstance(r.type, BinaryIteratorType)
|
|
460
460
|
and not xml_serializable(str(r.default_content_type))
|
|
461
|
+
and not (isinstance(r.type, ModelType) and r.type.is_typed_dict_only)
|
|
461
462
|
for r in self.responses
|
|
462
463
|
):
|
|
463
464
|
file_import.add_submodule_import(relative_path, "_deserialize", ImportType.LOCAL)
|
|
@@ -186,7 +186,11 @@ class PagingOperationBase(OperationBase[PagingResponseType]):
|
|
|
186
186
|
serialize_namespace, module_name="_utils.model_base"
|
|
187
187
|
)
|
|
188
188
|
file_import.merge(self.item_type.imports(**kwargs))
|
|
189
|
-
|
|
189
|
+
need_deserialize_import = self.default_error_deserialization(serialize_namespace) or (
|
|
190
|
+
self.need_deserialize
|
|
191
|
+
and not (isinstance(self.item_type, ModelType) and self.item_type.is_typed_dict_only)
|
|
192
|
+
)
|
|
193
|
+
if need_deserialize_import:
|
|
190
194
|
file_import.add_submodule_import(relative_path, "_deserialize", ImportType.LOCAL)
|
|
191
195
|
if self.is_xml_paging:
|
|
192
196
|
file_import.add_submodule_import("xml.etree", "ElementTree", ImportType.STDLIB, alias="ET")
|
|
@@ -180,7 +180,7 @@ class _ParameterBase(BaseModel, abc.ABC): # pylint: disable=too-many-instance-a
|
|
|
180
180
|
if isinstance(self.type, CombinedType) and self.type.name:
|
|
181
181
|
file_import.add_submodule_import(
|
|
182
182
|
self.code_model.get_relative_import_path(serialize_namespace),
|
|
183
|
-
"
|
|
183
|
+
"_unions",
|
|
184
184
|
ImportType.LOCAL,
|
|
185
185
|
TypingSection.TYPING,
|
|
186
186
|
)
|
|
@@ -338,7 +338,7 @@ class Parameter(_ParameterBase):
|
|
|
338
338
|
) -> ParameterMethodLocation:
|
|
339
339
|
if not self.in_method_signature:
|
|
340
340
|
raise ValueError(f"Parameter '{self.client_name}' is not in the method.")
|
|
341
|
-
if self.code_model.options["models-mode"]
|
|
341
|
+
if self.code_model.options["models-mode"] in ("dpg", "typeddict") and self.in_flattened_body:
|
|
342
342
|
return ParameterMethodLocation.KEYWORD_ONLY
|
|
343
343
|
if self.grouper:
|
|
344
344
|
return ParameterMethodLocation.POSITIONAL
|