@typespec/http-client-python 0.7.1 → 0.8.0

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.
Files changed (58) hide show
  1. package/dist/emitter/code-model.js +6 -6
  2. package/dist/emitter/code-model.js.map +1 -1
  3. package/dist/emitter/emitter.d.ts.map +1 -1
  4. package/dist/emitter/emitter.js +57 -54
  5. package/dist/emitter/emitter.js.map +1 -1
  6. package/dist/emitter/external-process.js +3 -3
  7. package/dist/emitter/external-process.js.map +1 -1
  8. package/dist/emitter/http.d.ts.map +1 -1
  9. package/dist/emitter/http.js +81 -23
  10. package/dist/emitter/http.js.map +1 -1
  11. package/dist/emitter/lib.d.ts +37 -1
  12. package/dist/emitter/lib.d.ts.map +1 -1
  13. package/dist/emitter/lib.js +24 -0
  14. package/dist/emitter/lib.js.map +1 -1
  15. package/dist/emitter/types.js +3 -3
  16. package/dist/emitter/types.js.map +1 -1
  17. package/dist/emitter/utils.d.ts +4 -2
  18. package/dist/emitter/utils.d.ts.map +1 -1
  19. package/dist/emitter/utils.js +21 -2
  20. package/dist/emitter/utils.js.map +1 -1
  21. package/emitter/src/code-model.ts +8 -8
  22. package/emitter/src/emitter.ts +57 -53
  23. package/emitter/src/external-process.ts +3 -3
  24. package/emitter/src/http.ts +113 -26
  25. package/emitter/src/lib.ts +24 -0
  26. package/emitter/src/types.ts +3 -3
  27. package/emitter/src/utils.ts +36 -1
  28. package/emitter/temp/tsconfig.tsbuildinfo +1 -1
  29. package/eng/scripts/setup/__pycache__/venvtools.cpython-38.pyc +0 -0
  30. package/eng/scripts/setup/run_tsp.py +2 -2
  31. package/generator/build/lib/pygen/__init__.py +3 -3
  32. package/generator/build/lib/pygen/black.py +2 -2
  33. package/generator/build/lib/pygen/codegen/__init__.py +5 -5
  34. package/generator/build/lib/pygen/codegen/models/paging_operation.py +11 -2
  35. package/generator/build/lib/pygen/codegen/models/parameter.py +2 -1
  36. package/generator/build/lib/pygen/codegen/models/request_builder_parameter.py +2 -0
  37. package/generator/build/lib/pygen/codegen/models/response.py +1 -1
  38. package/generator/build/lib/pygen/codegen/serializers/builder_serializer.py +47 -25
  39. package/generator/build/lib/pygen/preprocess/__init__.py +10 -12
  40. package/generator/build/lib/pygen/preprocess/python_mappings.py +1 -1
  41. package/generator/build/lib/pygen/utils.py +5 -5
  42. package/generator/component-detection-pip-report.json +7 -6
  43. package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
  44. package/generator/pygen/__init__.py +3 -3
  45. package/generator/pygen/black.py +2 -2
  46. package/generator/pygen/codegen/__init__.py +5 -5
  47. package/generator/pygen/codegen/models/paging_operation.py +11 -2
  48. package/generator/pygen/codegen/models/parameter.py +2 -1
  49. package/generator/pygen/codegen/models/request_builder_parameter.py +2 -0
  50. package/generator/pygen/codegen/models/response.py +1 -1
  51. package/generator/pygen/codegen/serializers/builder_serializer.py +47 -25
  52. package/generator/pygen/preprocess/__init__.py +10 -12
  53. package/generator/pygen/preprocess/python_mappings.py +1 -1
  54. package/generator/pygen/utils.py +5 -5
  55. package/generator/test/generic_mock_api_tests/asynctests/test_payload_pageable_async.py +75 -0
  56. package/generator/test/generic_mock_api_tests/test_payload_pageable.py +54 -0
  57. package/package.json +1 -1
  58. package/generator/test/unbranded/mock_api_tests/cadl-ranch-config.yaml +0 -27
@@ -79,7 +79,7 @@ class OptionsRetriever:
79
79
  @property
80
80
  def _models_mode_default(self) -> str:
81
81
  models_mode_default = "none" if self.low_level_client or self.version_tolerant else "msrest"
82
- if self.options.get("cadl_file") is not None:
82
+ if self.options.get("tsp_file") is not None:
83
83
  models_mode_default = "dpg"
84
84
  return models_mode_default
85
85
 
@@ -322,8 +322,8 @@ class CodeGenerator(Plugin):
322
322
  return {f: getattr(self.options_retriever, f) for f in flags}
323
323
 
324
324
  def get_yaml(self) -> Dict[str, Any]:
325
- # cadl file doesn't have to be relative to output folder
326
- with open(self.options["cadl_file"], "r", encoding="utf-8-sig") as fd:
325
+ # tsp file doesn't have to be relative to output folder
326
+ with open(self.options["tsp_file"], "r", encoding="utf-8-sig") as fd:
327
327
  return yaml.safe_load(fd.read())
328
328
 
329
329
  def get_serializer(self, code_model: CodeModel):
@@ -350,10 +350,10 @@ class CodeGenerator(Plugin):
350
350
 
351
351
 
352
352
  if __name__ == "__main__":
353
- # CADL pipeline will call this
353
+ # TSP pipeline will call this
354
354
  parsed_args, unknown_args = parse_args()
355
355
  CodeGenerator(
356
356
  output_folder=parsed_args.output_folder,
357
- cadl_file=parsed_args.cadl_file,
357
+ tsp_file=parsed_args.tsp_file,
358
358
  **unknown_args,
359
359
  ).process()
@@ -58,6 +58,15 @@ class PagingOperationBase(OperationBase[PagingResponseType]):
58
58
  self.override_success_response_to_200 = override_success_response_to_200
59
59
  self.pager_sync: str = yaml_data.get("pagerSync") or f"{self.code_model.core_library}.paging.ItemPaged"
60
60
  self.pager_async: str = yaml_data.get("pagerAsync") or f"{self.code_model.core_library}.paging.AsyncItemPaged"
61
+ self.continuation_token: Dict[str, Any] = yaml_data.get("continuationToken", {})
62
+
63
+ @property
64
+ def has_continuation_token(self) -> bool:
65
+ return bool(self.continuation_token.get("input") and self.continuation_token.get("output"))
66
+
67
+ @property
68
+ def next_variable_name(self) -> str:
69
+ return "_continuation_token" if self.has_continuation_token else "next_link"
61
70
 
62
71
  def _get_attr_name(self, wire_name: str) -> str:
63
72
  response_type = self.responses[0].type
@@ -74,8 +83,8 @@ class PagingOperationBase(OperationBase[PagingResponseType]):
74
83
  return self.responses[0].get_pager(async_mode)
75
84
 
76
85
  @property
77
- def continuation_token_name(self) -> Optional[str]:
78
- wire_name = self.yaml_data.get("continuationTokenName")
86
+ def next_link_name(self) -> Optional[str]:
87
+ wire_name = self.yaml_data.get("nextLinkName")
79
88
  if not wire_name:
80
89
  # That's an ok scenario, it just means no next page possible
81
90
  return None
@@ -85,6 +85,7 @@ class _ParameterBase(BaseModel, abc.ABC): # pylint: disable=too-many-instance-a
85
85
  self.in_overload: bool = self.yaml_data.get("inOverload", False)
86
86
  self.default_to_unset_sentinel: bool = self.yaml_data.get("defaultToUnsetSentinel", False)
87
87
  self.hide_in_method: bool = self.yaml_data.get("hideInMethod", False)
88
+ self.is_continuation_token: bool = bool(self.yaml_data.get("isContinuationToken"))
88
89
 
89
90
  def get_declaration(self, value: Any = None) -> Any:
90
91
  return self.type.get_declaration(value)
@@ -314,7 +315,7 @@ class Parameter(_ParameterBase):
314
315
  def hide_in_operation_signature(self) -> bool:
315
316
  if self.code_model.options["version_tolerant"] and self.client_name == "maxpagesize":
316
317
  return True
317
- return False
318
+ return self.is_continuation_token
318
319
 
319
320
  @property
320
321
  def in_method_signature(self) -> bool:
@@ -112,4 +112,6 @@ class RequestBuilderParameter(Parameter):
112
112
  return f"_{self.client_name}"
113
113
  if self.implementation == "Client":
114
114
  return f"self._config.{self.client_name}"
115
+ if self.is_continuation_token:
116
+ return "_continuation_token"
115
117
  return self.client_name
@@ -63,7 +63,7 @@ class Response(BaseModel):
63
63
  def result_property(self) -> str:
64
64
  field = self.yaml_data.get("resultProperty")
65
65
  if field:
66
- return f'.get("{field}")'
66
+ return "".join([f'.get("{field}", {{}})' for field in field.split(".")])
67
67
  return ""
68
68
 
69
69
  def get_polymorphic_subtypes(self, polymorphic_subtypes: List["ModelType"]) -> None:
@@ -630,7 +630,7 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
630
630
  operation_name=f"('{builder.name}')" if builder.group_name == "" else "",
631
631
  )
632
632
  for p in builder.parameters.parameters:
633
- if p.hide_in_operation_signature:
633
+ if p.hide_in_operation_signature and not p.is_continuation_token:
634
634
  kwargs.append(f'{p.client_name} = kwargs.pop("{p.client_name}", None)')
635
635
  cls_annotation = builder.cls_type_annotation(
636
636
  async_mode=self.async_mode, serialize_namespace=self.serialize_namespace
@@ -1001,9 +1001,7 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
1001
1001
  retval.extend(deserialize_code)
1002
1002
  return retval
1003
1003
 
1004
- def handle_error_response( # pylint: disable=too-many-statements, too-many-branches
1005
- self, builder: OperationType
1006
- ) -> List[str]:
1004
+ def handle_error_response(self, builder: OperationType) -> List[str]:
1007
1005
  async_await = "await " if self.async_mode else ""
1008
1006
  retval = [f"if response.status_code not in {str(builder.success_status_codes)}:"]
1009
1007
  response_read = [
@@ -1290,16 +1288,20 @@ class _PagingOperationSerializer(_OperationSerializer[PagingOperationType]):
1290
1288
 
1291
1289
  def _prepare_request_callback(self, builder: PagingOperationType) -> List[str]:
1292
1290
  retval = self._initialize_overloads(builder)
1293
- retval.append("def prepare_request(next_link=None):")
1294
- retval.append(" if not next_link:")
1295
- retval.extend([f" {line}" for line in self.call_request_builder(builder, is_paging=True)])
1296
- retval.append("")
1297
- retval.append(" else:")
1298
- retval.extend([f" {line}" for line in self.call_next_link_request_builder(builder)])
1299
- if not builder.next_request_builder and self.code_model.is_legacy:
1300
- retval.append(' _request.method = "GET"')
1291
+ if builder.has_continuation_token:
1292
+ retval.append(f"def prepare_request({builder.next_variable_name}=None):")
1293
+ retval.extend([f" {line}" for line in self.call_request_builder(builder, is_paging=True)])
1301
1294
  else:
1295
+ retval.append("def prepare_request(next_link=None):")
1296
+ retval.append(" if not next_link:")
1297
+ retval.extend([f" {line}" for line in self.call_request_builder(builder, is_paging=True)])
1302
1298
  retval.append("")
1299
+ retval.append(" else:")
1300
+ retval.extend([f" {line}" for line in self.call_next_link_request_builder(builder)])
1301
+ if not builder.next_request_builder and self.code_model.is_legacy:
1302
+ retval.append(' _request.method = "GET"')
1303
+ else:
1304
+ retval.append("")
1303
1305
  retval.append(" return _request")
1304
1306
  return retval
1305
1307
 
@@ -1307,7 +1309,7 @@ class _PagingOperationSerializer(_OperationSerializer[PagingOperationType]):
1307
1309
  def _function_def(self) -> str:
1308
1310
  return "def"
1309
1311
 
1310
- def _extract_data_callback(self, builder: PagingOperationType) -> List[str]:
1312
+ def _extract_data_callback(self, builder: PagingOperationType) -> List[str]: # pylint: disable=too-many-statements
1311
1313
  retval = [f"{'async ' if self.async_mode else ''}def extract_data(pipeline_response):"]
1312
1314
  response = builder.responses[0]
1313
1315
  deserialized = "pipeline_response.http_response.json()"
@@ -1328,7 +1330,13 @@ class _PagingOperationSerializer(_OperationSerializer[PagingOperationType]):
1328
1330
  else:
1329
1331
  retval.append(f" deserialized = {deserialized}")
1330
1332
  item_name = builder.item_name
1331
- access = f".{item_name}" if self.code_model.options["models_mode"] == "msrest" else f'["{item_name}"]'
1333
+ if self.code_model.options["models_mode"] == "msrest":
1334
+ access = f".{item_name}"
1335
+ else:
1336
+ item_name_array = item_name.split(".")
1337
+ access = (
1338
+ "".join([f'.get("{i}", {{}})' for i in item_name_array[:-1]]) + f'.get("{item_name_array[-1]}", [])'
1339
+ )
1332
1340
  list_of_elem_deserialized = ""
1333
1341
  if self.code_model.options["models_mode"] == "dpg":
1334
1342
  item_type = builder.item_type.type_annotation(
@@ -1341,20 +1349,34 @@ class _PagingOperationSerializer(_OperationSerializer[PagingOperationType]):
1341
1349
  retval.append(" if cls:")
1342
1350
  retval.append(" list_of_elem = cls(list_of_elem) # type: ignore")
1343
1351
 
1344
- continuation_token_name = builder.continuation_token_name
1345
- if not continuation_token_name:
1346
- cont_token_property = "None"
1347
- elif self.code_model.options["models_mode"] == "msrest":
1348
- cont_token_property = f"deserialized.{continuation_token_name} or None"
1352
+ if builder.has_continuation_token:
1353
+ location = builder.continuation_token.get("output", {}).get("location")
1354
+ wire_name = builder.continuation_token.get("output", {}).get("wireName") or ""
1355
+ if location == "header":
1356
+ cont_token_property = f'pipeline_response.http_response.headers.get("{wire_name}") or None'
1357
+ else:
1358
+ wire_name_array = wire_name.split(".")
1359
+ wire_name_call = (
1360
+ "".join([f'.get("{i}", {{}})' for i in wire_name_array[:-1]]) + f'.get("{wire_name_array[-1]}")'
1361
+ )
1362
+ cont_token_property = f"deserialized{wire_name_call} or None"
1349
1363
  else:
1350
- cont_token_property = f'deserialized.get("{continuation_token_name}") or None'
1364
+ next_link_name = builder.next_link_name
1365
+ if not next_link_name:
1366
+ cont_token_property = "None"
1367
+ elif self.code_model.options["models_mode"] == "msrest":
1368
+ cont_token_property = f"deserialized.{next_link_name} or None"
1369
+ else:
1370
+ cont_token_property = f'deserialized.get("{next_link_name}") or None'
1351
1371
  list_type = "AsyncList" if self.async_mode else "iter"
1352
1372
  retval.append(f" return {cont_token_property}, {list_type}(list_of_elem)")
1353
1373
  return retval
1354
1374
 
1355
1375
  def _get_next_callback(self, builder: PagingOperationType) -> List[str]:
1356
- retval = [f"{'async ' if self.async_mode else ''}def get_next(next_link=None):"]
1357
- retval.append(" _request = prepare_request(next_link)")
1376
+ retval = [
1377
+ f"{'async ' if self.async_mode else ''}def get_next({builder.next_variable_name}=None):" # pylint: disable=line-too-long
1378
+ ]
1379
+ retval.append(f" _request = prepare_request({builder.next_variable_name})")
1358
1380
  retval.append("")
1359
1381
  retval.extend([f" {l}" for l in self.make_pipeline_call(builder)])
1360
1382
  retval.append(" response = pipeline_response.http_response")
@@ -1506,10 +1528,10 @@ class LROPagingOperationSerializer(
1506
1528
 
1507
1529
  def get_long_running_output(self, builder: LROPagingOperation) -> List[str]:
1508
1530
  retval = ["def get_long_running_output(pipeline_response):"]
1509
- retval.append(f" {self._function_def} internal_get_next(next_link=None):")
1510
- retval.append(" if next_link is None:")
1531
+ retval.append(f" {self._function_def} internal_get_next({builder.next_variable_name}=None):")
1532
+ retval.append(f" if {builder.next_variable_name} is None:")
1511
1533
  retval.append(" return pipeline_response")
1512
- retval.append(f" return {self._call_method}get_next(next_link)")
1534
+ retval.append(f" return {self._call_method}get_next({builder.next_variable_name})")
1513
1535
  retval.append("")
1514
1536
  retval.append(f" return {builder.get_pager(self.async_mode)}(")
1515
1537
  retval.append(" internal_get_next, extract_data")
@@ -14,7 +14,7 @@ from .helpers import (
14
14
  pad_builtin_namespaces,
15
15
  pad_special_chars,
16
16
  )
17
- from .python_mappings import CADL_RESERVED_WORDS, RESERVED_WORDS, PadType
17
+ from .python_mappings import TSP_RESERVED_WORDS, RESERVED_WORDS, PadType
18
18
 
19
19
  from .. import YamlUpdatePlugin
20
20
  from ..utils import (
@@ -182,11 +182,11 @@ class PreProcessPlugin(YamlUpdatePlugin):
182
182
 
183
183
  @property
184
184
  def models_mode(self) -> Optional[str]:
185
- return self.options.get("models-mode", "dpg" if self.is_cadl else None)
185
+ return self.options.get("models-mode", "dpg" if self.is_tsp else None)
186
186
 
187
187
  @property
188
- def is_cadl(self) -> bool:
189
- return self.options.get("cadl_file", False)
188
+ def is_tsp(self) -> bool:
189
+ return self.options.get("tsp_file", False)
190
190
 
191
191
  def add_body_param_type(
192
192
  self,
@@ -197,9 +197,7 @@ class PreProcessPlugin(YamlUpdatePlugin):
197
197
  if ( # pylint: disable=too-many-boolean-expressions
198
198
  body_parameter
199
199
  and body_parameter["type"]["type"] in ("model", "dict", "list")
200
- and (
201
- has_json_content_type(body_parameter) or (self.is_cadl and has_multi_part_content_type(body_parameter))
202
- )
200
+ and (has_json_content_type(body_parameter) or (self.is_tsp and has_multi_part_content_type(body_parameter)))
203
201
  and not body_parameter["type"].get("xmlMetadata")
204
202
  and not any(t for t in ["flattened", "groupedBy"] if body_parameter.get(t))
205
203
  ):
@@ -210,7 +208,7 @@ class PreProcessPlugin(YamlUpdatePlugin):
210
208
  "types": [body_parameter["type"]],
211
209
  }
212
210
  # don't add binary overload for multipart content type
213
- if not (self.is_cadl and has_multi_part_content_type(body_parameter)):
211
+ if not (self.is_tsp and has_multi_part_content_type(body_parameter)):
214
212
  body_parameter["type"]["types"].append(KNOWN_TYPES["binary"])
215
213
 
216
214
  if origin_type == "model" and is_dpg_model and self.models_mode == "dpg":
@@ -223,8 +221,8 @@ class PreProcessPlugin(YamlUpdatePlugin):
223
221
  # we'll pass in empty operation groups sometime etc.
224
222
  return name
225
223
 
226
- if self.is_cadl:
227
- reserved_words = {k: (v + CADL_RESERVED_WORDS.get(k, [])) for k, v in RESERVED_WORDS.items()}
224
+ if self.is_tsp:
225
+ reserved_words = {k: (v + TSP_RESERVED_WORDS.get(k, [])) for k, v in RESERVED_WORDS.items()}
228
226
  else:
229
227
  reserved_words = RESERVED_WORDS
230
228
  name = pad_special_chars(name)
@@ -506,6 +504,6 @@ class PreProcessPlugin(YamlUpdatePlugin):
506
504
 
507
505
 
508
506
  if __name__ == "__main__":
509
- # CADL pipeline will call this
507
+ # TSP pipeline will call this
510
508
  args, unknown_args = parse_args()
511
- PreProcessPlugin(output_folder=args.output_folder, cadl_file=args.cadl_file, **unknown_args).process()
509
+ PreProcessPlugin(output_folder=args.output_folder, tsp_file=args.tsp_file, **unknown_args).process()
@@ -185,7 +185,7 @@ RESERVED_WORDS = {
185
185
  PadType.OPERATION_GROUP: [*_always_reserved],
186
186
  }
187
187
 
188
- CADL_RESERVED_WORDS = {
188
+ TSP_RESERVED_WORDS = {
189
189
  PadType.PARAMETER: ["stream"],
190
190
  PadType.PROPERTY: RESERVED_MODEL_PROPERTIES,
191
191
  }
@@ -44,7 +44,7 @@ def to_snake_case(name: str) -> str:
44
44
 
45
45
 
46
46
  def parse_args(
47
- need_cadl_file: bool = True,
47
+ need_tsp_file: bool = True,
48
48
  ) -> Tuple[argparse.Namespace, Dict[str, Any]]:
49
49
  parser = argparse.ArgumentParser(
50
50
  description="Run mypy against target folder. Add a local custom plugin to the path prior to execution. "
@@ -56,10 +56,10 @@ def parse_args(
56
56
  required=True,
57
57
  )
58
58
  parser.add_argument(
59
- "--cadl-file",
60
- dest="cadl_file",
61
- help="Serialized cadl file",
62
- required=need_cadl_file,
59
+ "--tsp-file",
60
+ dest="tsp_file",
61
+ help="Serialized tsp file",
62
+ required=need_tsp_file,
63
63
  )
64
64
  parser.add_argument(
65
65
  "--debug",
@@ -4,11 +4,11 @@
4
4
  "install": [
5
5
  {
6
6
  "download_info": {
7
- "url": "https://files.pythonhosted.org/packages/90/12/282ee9bce8b58130cb762fbc9beabd531549952cac11fc56add11dcb7ea0/setuptools-75.3.0-py3-none-any.whl",
7
+ "url": "https://files.pythonhosted.org/packages/15/65/3f0dba35760d902849d39d38c0a72767794b1963227b69a587f8a336d08c/setuptools-75.3.2-py3-none-any.whl",
8
8
  "archive_info": {
9
- "hash": "sha256=f2504966861356aa38616760c0f66568e535562374995367b4e69c7143cf6bcd",
9
+ "hash": "sha256=90ab613b6583fc02d5369cbca13ea26ea0e182d1df2d943ee9cbe81d4c61add9",
10
10
  "hashes": {
11
- "sha256": "f2504966861356aa38616760c0f66568e535562374995367b4e69c7143cf6bcd"
11
+ "sha256": "90ab613b6583fc02d5369cbca13ea26ea0e182d1df2d943ee9cbe81d4c61add9"
12
12
  }
13
13
  }
14
14
  },
@@ -18,7 +18,7 @@
18
18
  "metadata": {
19
19
  "metadata_version": "2.1",
20
20
  "name": "setuptools",
21
- "version": "75.3.0",
21
+ "version": "75.3.2",
22
22
  "summary": "Easily download, build, install, upgrade, and uninstall Python packages",
23
23
  "description_content_type": "text/x-rst",
24
24
  "keywords": [
@@ -93,6 +93,7 @@
93
93
  "pytest-subprocess ; extra == 'test'",
94
94
  "pyproject-hooks !=1.1 ; extra == 'test'",
95
95
  "jaraco.test >=5.5 ; extra == 'test'",
96
+ "ruff <=0.7.1 ; extra == 'test'",
96
97
  "jaraco.develop >=7.21 ; (python_version >= \"3.9\" and sys_platform != \"cygwin\") and extra == 'test'",
97
98
  "pytest-perf ; (sys_platform != \"cygwin\") and extra == 'test'",
98
99
  "pytest-mypy ; extra == 'type'",
@@ -126,9 +127,9 @@
126
127
  "implementation_version": "3.8.10",
127
128
  "os_name": "posix",
128
129
  "platform_machine": "x86_64",
129
- "platform_release": "5.15.0-1081-azure",
130
+ "platform_release": "5.15.0-1082-azure",
130
131
  "platform_system": "Linux",
131
- "platform_version": "#90~20.04.1-Ubuntu SMP Tue Jan 28 05:34:18 UTC 2025",
132
+ "platform_version": "#91~20.04.1-Ubuntu SMP Tue Feb 25 03:23:03 UTC 2025",
132
133
  "python_full_version": "3.8.10",
133
134
  "platform_python_implementation": "CPython",
134
135
  "python_version": "3.8",
@@ -78,12 +78,12 @@ class YamlUpdatePlugin(Plugin):
78
78
  """A plugin that update the YAML as input."""
79
79
 
80
80
  def get_yaml(self) -> Dict[str, Any]:
81
- # cadl file doesn't have to be relative to output folder
82
- with open(self.options["cadl_file"], "r", encoding="utf-8-sig") as fd:
81
+ # tsp file doesn't have to be relative to output folder
82
+ with open(self.options["tsp_file"], "r", encoding="utf-8-sig") as fd:
83
83
  return yaml.safe_load(fd.read())
84
84
 
85
85
  def write_yaml(self, yaml_string: str) -> None:
86
- with open(self.options["cadl_file"], "w", encoding="utf-8-sig") as fd:
86
+ with open(self.options["tsp_file"], "w", encoding="utf-8-sig") as fd:
87
87
  fd.write(yaml_string)
88
88
 
89
89
  def process(self) -> bool:
@@ -77,6 +77,6 @@ class BlackScriptPlugin(Plugin):
77
77
 
78
78
 
79
79
  if __name__ == "__main__":
80
- # CADL pipeline will call this
81
- args, unknown_args = parse_args(need_cadl_file=False)
80
+ # TSP pipeline will call this
81
+ args, unknown_args = parse_args(need_tsp_file=False)
82
82
  BlackScriptPlugin(output_folder=args.output_folder, **unknown_args).process()
@@ -79,7 +79,7 @@ class OptionsRetriever:
79
79
  @property
80
80
  def _models_mode_default(self) -> str:
81
81
  models_mode_default = "none" if self.low_level_client or self.version_tolerant else "msrest"
82
- if self.options.get("cadl_file") is not None:
82
+ if self.options.get("tsp_file") is not None:
83
83
  models_mode_default = "dpg"
84
84
  return models_mode_default
85
85
 
@@ -322,8 +322,8 @@ class CodeGenerator(Plugin):
322
322
  return {f: getattr(self.options_retriever, f) for f in flags}
323
323
 
324
324
  def get_yaml(self) -> Dict[str, Any]:
325
- # cadl file doesn't have to be relative to output folder
326
- with open(self.options["cadl_file"], "r", encoding="utf-8-sig") as fd:
325
+ # tsp file doesn't have to be relative to output folder
326
+ with open(self.options["tsp_file"], "r", encoding="utf-8-sig") as fd:
327
327
  return yaml.safe_load(fd.read())
328
328
 
329
329
  def get_serializer(self, code_model: CodeModel):
@@ -350,10 +350,10 @@ class CodeGenerator(Plugin):
350
350
 
351
351
 
352
352
  if __name__ == "__main__":
353
- # CADL pipeline will call this
353
+ # TSP pipeline will call this
354
354
  parsed_args, unknown_args = parse_args()
355
355
  CodeGenerator(
356
356
  output_folder=parsed_args.output_folder,
357
- cadl_file=parsed_args.cadl_file,
357
+ tsp_file=parsed_args.tsp_file,
358
358
  **unknown_args,
359
359
  ).process()
@@ -58,6 +58,15 @@ class PagingOperationBase(OperationBase[PagingResponseType]):
58
58
  self.override_success_response_to_200 = override_success_response_to_200
59
59
  self.pager_sync: str = yaml_data.get("pagerSync") or f"{self.code_model.core_library}.paging.ItemPaged"
60
60
  self.pager_async: str = yaml_data.get("pagerAsync") or f"{self.code_model.core_library}.paging.AsyncItemPaged"
61
+ self.continuation_token: Dict[str, Any] = yaml_data.get("continuationToken", {})
62
+
63
+ @property
64
+ def has_continuation_token(self) -> bool:
65
+ return bool(self.continuation_token.get("input") and self.continuation_token.get("output"))
66
+
67
+ @property
68
+ def next_variable_name(self) -> str:
69
+ return "_continuation_token" if self.has_continuation_token else "next_link"
61
70
 
62
71
  def _get_attr_name(self, wire_name: str) -> str:
63
72
  response_type = self.responses[0].type
@@ -74,8 +83,8 @@ class PagingOperationBase(OperationBase[PagingResponseType]):
74
83
  return self.responses[0].get_pager(async_mode)
75
84
 
76
85
  @property
77
- def continuation_token_name(self) -> Optional[str]:
78
- wire_name = self.yaml_data.get("continuationTokenName")
86
+ def next_link_name(self) -> Optional[str]:
87
+ wire_name = self.yaml_data.get("nextLinkName")
79
88
  if not wire_name:
80
89
  # That's an ok scenario, it just means no next page possible
81
90
  return None
@@ -85,6 +85,7 @@ class _ParameterBase(BaseModel, abc.ABC): # pylint: disable=too-many-instance-a
85
85
  self.in_overload: bool = self.yaml_data.get("inOverload", False)
86
86
  self.default_to_unset_sentinel: bool = self.yaml_data.get("defaultToUnsetSentinel", False)
87
87
  self.hide_in_method: bool = self.yaml_data.get("hideInMethod", False)
88
+ self.is_continuation_token: bool = bool(self.yaml_data.get("isContinuationToken"))
88
89
 
89
90
  def get_declaration(self, value: Any = None) -> Any:
90
91
  return self.type.get_declaration(value)
@@ -314,7 +315,7 @@ class Parameter(_ParameterBase):
314
315
  def hide_in_operation_signature(self) -> bool:
315
316
  if self.code_model.options["version_tolerant"] and self.client_name == "maxpagesize":
316
317
  return True
317
- return False
318
+ return self.is_continuation_token
318
319
 
319
320
  @property
320
321
  def in_method_signature(self) -> bool:
@@ -112,4 +112,6 @@ class RequestBuilderParameter(Parameter):
112
112
  return f"_{self.client_name}"
113
113
  if self.implementation == "Client":
114
114
  return f"self._config.{self.client_name}"
115
+ if self.is_continuation_token:
116
+ return "_continuation_token"
115
117
  return self.client_name
@@ -63,7 +63,7 @@ class Response(BaseModel):
63
63
  def result_property(self) -> str:
64
64
  field = self.yaml_data.get("resultProperty")
65
65
  if field:
66
- return f'.get("{field}")'
66
+ return "".join([f'.get("{field}", {{}})' for field in field.split(".")])
67
67
  return ""
68
68
 
69
69
  def get_polymorphic_subtypes(self, polymorphic_subtypes: List["ModelType"]) -> None:
@@ -630,7 +630,7 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
630
630
  operation_name=f"('{builder.name}')" if builder.group_name == "" else "",
631
631
  )
632
632
  for p in builder.parameters.parameters:
633
- if p.hide_in_operation_signature:
633
+ if p.hide_in_operation_signature and not p.is_continuation_token:
634
634
  kwargs.append(f'{p.client_name} = kwargs.pop("{p.client_name}", None)')
635
635
  cls_annotation = builder.cls_type_annotation(
636
636
  async_mode=self.async_mode, serialize_namespace=self.serialize_namespace
@@ -1001,9 +1001,7 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
1001
1001
  retval.extend(deserialize_code)
1002
1002
  return retval
1003
1003
 
1004
- def handle_error_response( # pylint: disable=too-many-statements, too-many-branches
1005
- self, builder: OperationType
1006
- ) -> List[str]:
1004
+ def handle_error_response(self, builder: OperationType) -> List[str]:
1007
1005
  async_await = "await " if self.async_mode else ""
1008
1006
  retval = [f"if response.status_code not in {str(builder.success_status_codes)}:"]
1009
1007
  response_read = [
@@ -1290,16 +1288,20 @@ class _PagingOperationSerializer(_OperationSerializer[PagingOperationType]):
1290
1288
 
1291
1289
  def _prepare_request_callback(self, builder: PagingOperationType) -> List[str]:
1292
1290
  retval = self._initialize_overloads(builder)
1293
- retval.append("def prepare_request(next_link=None):")
1294
- retval.append(" if not next_link:")
1295
- retval.extend([f" {line}" for line in self.call_request_builder(builder, is_paging=True)])
1296
- retval.append("")
1297
- retval.append(" else:")
1298
- retval.extend([f" {line}" for line in self.call_next_link_request_builder(builder)])
1299
- if not builder.next_request_builder and self.code_model.is_legacy:
1300
- retval.append(' _request.method = "GET"')
1291
+ if builder.has_continuation_token:
1292
+ retval.append(f"def prepare_request({builder.next_variable_name}=None):")
1293
+ retval.extend([f" {line}" for line in self.call_request_builder(builder, is_paging=True)])
1301
1294
  else:
1295
+ retval.append("def prepare_request(next_link=None):")
1296
+ retval.append(" if not next_link:")
1297
+ retval.extend([f" {line}" for line in self.call_request_builder(builder, is_paging=True)])
1302
1298
  retval.append("")
1299
+ retval.append(" else:")
1300
+ retval.extend([f" {line}" for line in self.call_next_link_request_builder(builder)])
1301
+ if not builder.next_request_builder and self.code_model.is_legacy:
1302
+ retval.append(' _request.method = "GET"')
1303
+ else:
1304
+ retval.append("")
1303
1305
  retval.append(" return _request")
1304
1306
  return retval
1305
1307
 
@@ -1307,7 +1309,7 @@ class _PagingOperationSerializer(_OperationSerializer[PagingOperationType]):
1307
1309
  def _function_def(self) -> str:
1308
1310
  return "def"
1309
1311
 
1310
- def _extract_data_callback(self, builder: PagingOperationType) -> List[str]:
1312
+ def _extract_data_callback(self, builder: PagingOperationType) -> List[str]: # pylint: disable=too-many-statements
1311
1313
  retval = [f"{'async ' if self.async_mode else ''}def extract_data(pipeline_response):"]
1312
1314
  response = builder.responses[0]
1313
1315
  deserialized = "pipeline_response.http_response.json()"
@@ -1328,7 +1330,13 @@ class _PagingOperationSerializer(_OperationSerializer[PagingOperationType]):
1328
1330
  else:
1329
1331
  retval.append(f" deserialized = {deserialized}")
1330
1332
  item_name = builder.item_name
1331
- access = f".{item_name}" if self.code_model.options["models_mode"] == "msrest" else f'["{item_name}"]'
1333
+ if self.code_model.options["models_mode"] == "msrest":
1334
+ access = f".{item_name}"
1335
+ else:
1336
+ item_name_array = item_name.split(".")
1337
+ access = (
1338
+ "".join([f'.get("{i}", {{}})' for i in item_name_array[:-1]]) + f'.get("{item_name_array[-1]}", [])'
1339
+ )
1332
1340
  list_of_elem_deserialized = ""
1333
1341
  if self.code_model.options["models_mode"] == "dpg":
1334
1342
  item_type = builder.item_type.type_annotation(
@@ -1341,20 +1349,34 @@ class _PagingOperationSerializer(_OperationSerializer[PagingOperationType]):
1341
1349
  retval.append(" if cls:")
1342
1350
  retval.append(" list_of_elem = cls(list_of_elem) # type: ignore")
1343
1351
 
1344
- continuation_token_name = builder.continuation_token_name
1345
- if not continuation_token_name:
1346
- cont_token_property = "None"
1347
- elif self.code_model.options["models_mode"] == "msrest":
1348
- cont_token_property = f"deserialized.{continuation_token_name} or None"
1352
+ if builder.has_continuation_token:
1353
+ location = builder.continuation_token.get("output", {}).get("location")
1354
+ wire_name = builder.continuation_token.get("output", {}).get("wireName") or ""
1355
+ if location == "header":
1356
+ cont_token_property = f'pipeline_response.http_response.headers.get("{wire_name}") or None'
1357
+ else:
1358
+ wire_name_array = wire_name.split(".")
1359
+ wire_name_call = (
1360
+ "".join([f'.get("{i}", {{}})' for i in wire_name_array[:-1]]) + f'.get("{wire_name_array[-1]}")'
1361
+ )
1362
+ cont_token_property = f"deserialized{wire_name_call} or None"
1349
1363
  else:
1350
- cont_token_property = f'deserialized.get("{continuation_token_name}") or None'
1364
+ next_link_name = builder.next_link_name
1365
+ if not next_link_name:
1366
+ cont_token_property = "None"
1367
+ elif self.code_model.options["models_mode"] == "msrest":
1368
+ cont_token_property = f"deserialized.{next_link_name} or None"
1369
+ else:
1370
+ cont_token_property = f'deserialized.get("{next_link_name}") or None'
1351
1371
  list_type = "AsyncList" if self.async_mode else "iter"
1352
1372
  retval.append(f" return {cont_token_property}, {list_type}(list_of_elem)")
1353
1373
  return retval
1354
1374
 
1355
1375
  def _get_next_callback(self, builder: PagingOperationType) -> List[str]:
1356
- retval = [f"{'async ' if self.async_mode else ''}def get_next(next_link=None):"]
1357
- retval.append(" _request = prepare_request(next_link)")
1376
+ retval = [
1377
+ f"{'async ' if self.async_mode else ''}def get_next({builder.next_variable_name}=None):" # pylint: disable=line-too-long
1378
+ ]
1379
+ retval.append(f" _request = prepare_request({builder.next_variable_name})")
1358
1380
  retval.append("")
1359
1381
  retval.extend([f" {l}" for l in self.make_pipeline_call(builder)])
1360
1382
  retval.append(" response = pipeline_response.http_response")
@@ -1506,10 +1528,10 @@ class LROPagingOperationSerializer(
1506
1528
 
1507
1529
  def get_long_running_output(self, builder: LROPagingOperation) -> List[str]:
1508
1530
  retval = ["def get_long_running_output(pipeline_response):"]
1509
- retval.append(f" {self._function_def} internal_get_next(next_link=None):")
1510
- retval.append(" if next_link is None:")
1531
+ retval.append(f" {self._function_def} internal_get_next({builder.next_variable_name}=None):")
1532
+ retval.append(f" if {builder.next_variable_name} is None:")
1511
1533
  retval.append(" return pipeline_response")
1512
- retval.append(f" return {self._call_method}get_next(next_link)")
1534
+ retval.append(f" return {self._call_method}get_next({builder.next_variable_name})")
1513
1535
  retval.append("")
1514
1536
  retval.append(f" return {builder.get_pager(self.async_mode)}(")
1515
1537
  retval.append(" internal_get_next, extract_data")