@autorest/python 6.41.3 → 6.42.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.
@@ -3,6 +3,7 @@
3
3
  # Licensed under the MIT License. See License.txt in the project root for
4
4
  # license information.
5
5
  # --------------------------------------------------------------------------
6
+ import shutil
6
7
  from collections.abc import ItemsView, KeysView, MutableMapping, ValuesView
7
8
  import logging
8
9
  from pathlib import Path
@@ -248,9 +249,23 @@ class ReaderAndWriter:
248
249
  except FileNotFoundError:
249
250
  pass
250
251
 
252
+ def remove_folder(self, foldername: Union[str, Path]) -> None:
253
+ try:
254
+ folder_path = self.output_folder / Path(foldername)
255
+ if folder_path.exists() and folder_path.is_dir():
256
+ shutil.rmtree(folder_path)
257
+ except FileNotFoundError:
258
+ pass
259
+
251
260
  def list_file(self) -> list[str]:
252
261
  return [str(f.relative_to(self.output_folder)) for f in self.output_folder.glob("**/*") if f.is_file()]
253
262
 
263
+ def list_file_of_folder(self, foldername: Union[str, Path]) -> list[str]:
264
+ folder_path = self.output_folder / Path(foldername)
265
+ if folder_path.exists() and folder_path.is_dir():
266
+ return [str(f.relative_to(self.output_folder)) for f in folder_path.glob("**/*") if f.is_file()]
267
+ return []
268
+
254
269
 
255
270
  class Plugin(ReaderAndWriter, ABC):
256
271
  """A base class for autorest plugin.
@@ -200,10 +200,10 @@ class OperationBase( # pylint: disable=too-many-public-methods,too-many-instanc
200
200
  if isinstance(exception_schema, ModelType):
201
201
  pylint_disable = " # pylint: disable=protected-access" if exception_schema.internal else ""
202
202
  return (
203
- exception_schema.type_annotation(skip_quote=True, serialize_namespace=serialize_namespace)
204
- + pylint_disable
203
+ f"{exception_schema.type_annotation(skip_quote=True, serialize_namespace=serialize_namespace)},"
204
+ f"{pylint_disable}"
205
205
  )
206
- return None if self.code_model.options["models-mode"] == "dpg" else "'object'"
206
+ return None if self.code_model.options["models-mode"] == "dpg" else "'object',"
207
207
 
208
208
  @property
209
209
  def non_default_errors(self) -> list[Response]:
@@ -122,7 +122,21 @@ class JinjaSerializer(ReaderAndWriter):
122
122
  # If parsing the version fails, we assume the version file is not valid and overwrite.
123
123
  return False
124
124
 
125
+ # pylint: disable=too-many-branches
125
126
  def serialize(self) -> None:
127
+ # remove existing folders when generate from tsp
128
+ if self.code_model.is_tsp and self.code_model.is_azure_flavor:
129
+ # remove generated_samples and generated_tests folder
130
+ self.remove_folder(self._generated_tests_samples_folder("generated_samples"))
131
+ self.remove_folder(self._generated_tests_samples_folder("generated_tests"))
132
+
133
+ # remove generated sdk files
134
+ generation_path = self.code_model.get_generation_dir(self.code_model.namespace)
135
+ for file in self.list_file_of_folder(generation_path):
136
+ if file.endswith(".py") and "_patch.py" not in file:
137
+ self.remove_file(file)
138
+
139
+ # serialize logic
126
140
  env = Environment(
127
141
  loader=PackageLoader("pygen.codegen", "templates"),
128
142
  keep_trailing_newline=True,
@@ -519,8 +533,11 @@ class JinjaSerializer(ReaderAndWriter):
519
533
  return Path("/".join(namespace_config.split(".")[num_of_package_namespace:]))
520
534
  return Path("")
521
535
 
536
+ def _generated_tests_samples_folder(self, folder_name: str) -> Path:
537
+ return self._root_of_sdk / folder_name
538
+
522
539
  def _serialize_and_write_sample(self, env: Environment):
523
- out_path = self._root_of_sdk / "generated_samples"
540
+ out_path = self._generated_tests_samples_folder("generated_samples")
524
541
  for client in self.code_model.clients:
525
542
  for op_group in client.operation_groups:
526
543
  for operation in op_group.operations:
@@ -549,7 +566,7 @@ class JinjaSerializer(ReaderAndWriter):
549
566
 
550
567
  def _serialize_and_write_test(self, env: Environment):
551
568
  self.code_model.for_test = True
552
- out_path = self._root_of_sdk / "generated_tests"
569
+ out_path = self._generated_tests_samples_folder("generated_tests")
553
570
  general_serializer = TestGeneralSerializer(code_model=self.code_model, env=env)
554
571
  self.write_file(out_path / "conftest.py", general_serializer.serialize_conftest())
555
572
  if not self.code_model.options["azure-arm"]:
@@ -1053,10 +1053,13 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
1053
1053
  f" error = _failsafe_deserialize({type_annotation},{pylint_disable}\n response)"
1054
1054
  )
1055
1055
  else:
1056
- retval.append(
1057
- " error = self._deserialize.failsafe_deserialize("
1058
- f"{type_annotation},{pylint_disable}\n "
1059
- "pipeline_response)"
1056
+ retval.extend(
1057
+ [
1058
+ " error = self._deserialize.failsafe_deserialize(",
1059
+ f" {type_annotation},{pylint_disable}",
1060
+ " pipeline_response,",
1061
+ " )",
1062
+ ]
1060
1063
  )
1061
1064
  # add build-in error type
1062
1065
  # TODO: we should decide whether need to this wrapper for customized error type
@@ -1097,10 +1100,13 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
1097
1100
  f"{type_annotation},{pylint_disable}\n response)"
1098
1101
  )
1099
1102
  else:
1100
- retval.append(
1101
- " error = self._deserialize.failsafe_deserialize("
1102
- f"{type_annotation},{pylint_disable}\n "
1103
- "pipeline_response)"
1103
+ retval.extend(
1104
+ [
1105
+ " error = self._deserialize.failsafe_deserialize(",
1106
+ f" {type_annotation},{pylint_disable}",
1107
+ " pipeline_response,",
1108
+ " )",
1109
+ ]
1104
1110
  )
1105
1111
  condition = "elif"
1106
1112
  # default error handling
@@ -1111,11 +1117,22 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
1111
1117
  if builder.non_default_errors:
1112
1118
  retval.append(" else:")
1113
1119
  if self.code_model.options["models-mode"] == "dpg":
1114
- retval.append(f"{indent}error = _failsafe_deserialize({default_error_deserialization}, response)")
1120
+ retval.extend(
1121
+ [
1122
+ f"{indent}error = _failsafe_deserialize(",
1123
+ f"{indent} {default_error_deserialization}",
1124
+ f"{indent} response,",
1125
+ f"{indent})",
1126
+ ]
1127
+ )
1115
1128
  else:
1116
- retval.append(
1117
- f"{indent}error = self._deserialize.failsafe_deserialize({default_error_deserialization}, "
1118
- "pipeline_response)"
1129
+ retval.extend(
1130
+ [
1131
+ f"{indent}error = self._deserialize.failsafe_deserialize(",
1132
+ f"{indent} {default_error_deserialization}",
1133
+ f"{indent} pipeline_response,",
1134
+ f"{indent})",
1135
+ ]
1119
1136
  )
1120
1137
  retval.append(
1121
1138
  " raise HttpResponseError(response=response{}{})".format(
@@ -202,9 +202,11 @@ class ParameterSerializer:
202
202
  if is_content_type_optional and not type_annotation.startswith("Optional[")
203
203
  else type_annotation
204
204
  )
205
- if kwarg.client_default_value is not None or kwarg.optional:
205
+ if kwarg.client_default_value is not None or kwarg.optional or kwarg.constant:
206
206
  if check_client_input and kwarg.check_client_input:
207
207
  default_value = f"self._config.{kwarg.client_name}"
208
+ elif kwarg.constant:
209
+ default_value = kwarg.type.get_declaration(None)
208
210
  else:
209
211
  default_value = kwarg.client_default_value_declaration
210
212
  if check_kwarg_dict and (kwarg.location in [ParameterLocation.HEADER, ParameterLocation.QUERY]):
@@ -1,21 +1,39 @@
1
1
  {% macro wrap_model_string(doc_string, wrap_string, suffix_string="") %}
2
- {%- set lines = doc_string.split('\n') -%}
3
- {%- set processed_lines = [] -%}
4
- {%- for line in lines -%}
5
- {%- set stripped = line.strip() -%}
6
- {%- if stripped.startswith('* ') -%}
7
- {%- set bullet_with_indent = ' ' + stripped -%}
8
- {%- set wrapped = bullet_with_indent | wordwrap(width=95, break_long_words=False, break_on_hyphens=False, wrapstring=wrap_string + ' ') -%}
9
- {%- set _ = processed_lines.append(wrapped) -%}
10
- {%- elif stripped -%}
11
- {%- set wrapped = line | wordwrap(width=95, break_long_words=False, break_on_hyphens=False, wrapstring=wrap_string) -%}
12
- {%- set _ = processed_lines.append(wrapped) -%}
13
- {%- endif -%}
14
- {%- endfor -%}
15
- {%- set original_result = processed_lines | join('\n') -%}
2
+ {# Check if this is a sphinx documentation line that should not have extra prefix spacing #}
3
+ {%- set is_sphinx_doc = doc_string.strip().startswith(':ivar') or doc_string.strip().startswith(':vartype') or doc_string.strip().startswith(':param') or doc_string.strip().startswith(':type') -%}
4
+ {# Custom handling for bullet points - normalization is now done in preprocessing #}
5
+ {% set enable_custom_handling = "\n* " in doc_string or doc_string.startswith("* ") %}
6
+ {%- if enable_custom_handling -%}
7
+ {%- set lines = doc_string.split('\n') -%}
8
+ {%- set result_lines = [] -%}
9
+ {%- for line in lines -%}
10
+ {%- if line.startswith('* ') -%}
11
+ {# Handle bullet points with proper continuation alignment #}
12
+ {%- set bullet_content = line[2:] -%}
13
+ {%- set base_indent = wrap_string.lstrip('\n') -%}
14
+ {%- set bullet_line = base_indent + ' * ' + bullet_content -%}
15
+ {%- set continuation_spaces = base_indent + ' ' -%}
16
+ {%- set wrapped = bullet_line | wordwrap(width=95, break_long_words=False, break_on_hyphens=False, wrapstring='\n' + continuation_spaces) -%}
17
+ {%- set _ = result_lines.append(wrapped) -%}
18
+ {%- elif line.strip() -%}
19
+ {%- set wrapped = line.strip() | wordwrap(width=95, break_long_words=False, break_on_hyphens=False, wrapstring=wrap_string) -%}
20
+ {%- set _ = result_lines.append(wrapped) -%}
21
+ {%- else -%}
22
+ {%- set _ = result_lines.append('') -%}
23
+ {%- endif -%}
24
+ {%- endfor -%}
25
+ {%- set original_result = result_lines | join('\n') -%}
26
+ {%- else -%}
27
+ {# Regular text handling #}
28
+ {%- set original_result = doc_string | wordwrap(width=95, break_long_words=False, break_on_hyphens=False, wrapstring=wrap_string) -%}
29
+ {%- endif -%}
16
30
  {% set list_result = original_result.split('\n') %}
17
31
  {% for line in list_result %}
18
- {% set prefix = "" if loop.index == 1 else " " %}
32
+ {%- if is_sphinx_doc and enable_custom_handling -%}
33
+ {%- set prefix = "" -%}
34
+ {%- else -%}
35
+ {%- set prefix = "" if loop.index == 1 else " " -%}
36
+ {%- endif -%}
19
37
  {% set suffix = suffix_string if list_result | length == loop.index %}
20
38
  {{ prefix }}{{ line }}{{ suffix }}
21
39
  {% endfor %}
@@ -78,7 +78,7 @@
78
78
  if key in self.__flattened_items:
79
79
  if self.{{ flattened_property_attr }} is None:
80
80
  self.{{ flattened_property_attr }} = self._attr_to_rest_field["{{ flattened_property_attr }}"]._class_type()
81
- setattr(self.properties, key, value)
81
+ setattr(self.{{ flattened_property_attr }}, key, value)
82
82
  else:
83
83
  super().__setattr__(key, value)
84
84
  {% endif %}
@@ -1,18 +1,4 @@
1
- {% macro wrap_string(string, wrapstring, width=95) %}
2
- {%- set lines = string.split('\n') -%}
3
- {%- set processed_lines = [] -%}
4
- {%- for line in lines -%}
5
- {%- set stripped = line.strip() -%}
6
- {%- if stripped.startswith('* ') -%}
7
- {%- set bullet_with_indent = stripped -%}
8
- {%- set wrapped = bullet_with_indent | wordwrap(width=width, break_long_words=False, break_on_hyphens=False, wrapstring=wrapstring + ' ') -%}
9
- {%- set _ = processed_lines.append(wrapped) -%}
10
- {%- elif stripped -%}
11
- {%- set wrapped = line | wordwrap(width=width, break_long_words=False, break_on_hyphens=False, wrapstring=wrapstring) -%}
12
- {%- set _ = processed_lines.append(wrapped) -%}
13
- {%- endif -%}
14
- {%- endfor -%}
15
- {{ processed_lines | join('\n') | replace("\\", "\\\\") }}{%- endmacro %}
1
+ {% macro wrap_string(string, wrapstring, width=95) %}{{ string | replace("\\", "\\\\") | wordwrap(width=width, break_long_words=False, break_on_hyphens=False, wrapstring=wrapstring)}}{% endmacro %}
16
2
 
17
3
  {% macro description(builder, serializer) %}
18
4
  {% set example_template = serializer.example_template(builder) %}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "1",
3
- "pip_version": "25.2",
3
+ "pip_version": "25.3",
4
4
  "install": [
5
5
  {
6
6
  "download_info": {
@@ -23,6 +23,7 @@
23
23
  "license-file"
24
24
  ],
25
25
  "summary": "Easily download, build, install, upgrade, and uninstall Python packages",
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-2025-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",
26
27
  "description_content_type": "text/x-rst",
27
28
  "keywords": [
28
29
  "CPAN",
@@ -116,22 +117,21 @@
116
117
  "cover",
117
118
  "enabler",
118
119
  "type"
119
- ],
120
- "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-2025-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"
120
+ ]
121
121
  }
122
122
  }
123
123
  ],
124
124
  "environment": {
125
125
  "implementation_name": "cpython",
126
- "implementation_version": "3.9.23",
126
+ "implementation_version": "3.11.13",
127
127
  "os_name": "posix",
128
128
  "platform_machine": "x86_64",
129
129
  "platform_release": "6.11.0-1018-azure",
130
130
  "platform_system": "Linux",
131
131
  "platform_version": "#18~24.04.1-Ubuntu SMP Sat Jun 28 04:46:03 UTC 2025",
132
- "python_full_version": "3.9.23",
132
+ "python_full_version": "3.11.13",
133
133
  "platform_python_implementation": "CPython",
134
- "python_version": "3.9",
134
+ "python_version": "3.11",
135
135
  "sys_platform": "linux"
136
136
  }
137
137
  }
@@ -3,6 +3,7 @@
3
3
  # Licensed under the MIT License. See License.txt in the project root for
4
4
  # license information.
5
5
  # --------------------------------------------------------------------------
6
+ import shutil
6
7
  from collections.abc import ItemsView, KeysView, MutableMapping, ValuesView
7
8
  import logging
8
9
  from pathlib import Path
@@ -248,9 +249,23 @@ class ReaderAndWriter:
248
249
  except FileNotFoundError:
249
250
  pass
250
251
 
252
+ def remove_folder(self, foldername: Union[str, Path]) -> None:
253
+ try:
254
+ folder_path = self.output_folder / Path(foldername)
255
+ if folder_path.exists() and folder_path.is_dir():
256
+ shutil.rmtree(folder_path)
257
+ except FileNotFoundError:
258
+ pass
259
+
251
260
  def list_file(self) -> list[str]:
252
261
  return [str(f.relative_to(self.output_folder)) for f in self.output_folder.glob("**/*") if f.is_file()]
253
262
 
263
+ def list_file_of_folder(self, foldername: Union[str, Path]) -> list[str]:
264
+ folder_path = self.output_folder / Path(foldername)
265
+ if folder_path.exists() and folder_path.is_dir():
266
+ return [str(f.relative_to(self.output_folder)) for f in folder_path.glob("**/*") if f.is_file()]
267
+ return []
268
+
254
269
 
255
270
  class Plugin(ReaderAndWriter, ABC):
256
271
  """A base class for autorest plugin.
@@ -200,10 +200,10 @@ class OperationBase( # pylint: disable=too-many-public-methods,too-many-instanc
200
200
  if isinstance(exception_schema, ModelType):
201
201
  pylint_disable = " # pylint: disable=protected-access" if exception_schema.internal else ""
202
202
  return (
203
- exception_schema.type_annotation(skip_quote=True, serialize_namespace=serialize_namespace)
204
- + pylint_disable
203
+ f"{exception_schema.type_annotation(skip_quote=True, serialize_namespace=serialize_namespace)},"
204
+ f"{pylint_disable}"
205
205
  )
206
- return None if self.code_model.options["models-mode"] == "dpg" else "'object'"
206
+ return None if self.code_model.options["models-mode"] == "dpg" else "'object',"
207
207
 
208
208
  @property
209
209
  def non_default_errors(self) -> list[Response]:
@@ -122,7 +122,21 @@ class JinjaSerializer(ReaderAndWriter):
122
122
  # If parsing the version fails, we assume the version file is not valid and overwrite.
123
123
  return False
124
124
 
125
+ # pylint: disable=too-many-branches
125
126
  def serialize(self) -> None:
127
+ # remove existing folders when generate from tsp
128
+ if self.code_model.is_tsp and self.code_model.is_azure_flavor:
129
+ # remove generated_samples and generated_tests folder
130
+ self.remove_folder(self._generated_tests_samples_folder("generated_samples"))
131
+ self.remove_folder(self._generated_tests_samples_folder("generated_tests"))
132
+
133
+ # remove generated sdk files
134
+ generation_path = self.code_model.get_generation_dir(self.code_model.namespace)
135
+ for file in self.list_file_of_folder(generation_path):
136
+ if file.endswith(".py") and "_patch.py" not in file:
137
+ self.remove_file(file)
138
+
139
+ # serialize logic
126
140
  env = Environment(
127
141
  loader=PackageLoader("pygen.codegen", "templates"),
128
142
  keep_trailing_newline=True,
@@ -519,8 +533,11 @@ class JinjaSerializer(ReaderAndWriter):
519
533
  return Path("/".join(namespace_config.split(".")[num_of_package_namespace:]))
520
534
  return Path("")
521
535
 
536
+ def _generated_tests_samples_folder(self, folder_name: str) -> Path:
537
+ return self._root_of_sdk / folder_name
538
+
522
539
  def _serialize_and_write_sample(self, env: Environment):
523
- out_path = self._root_of_sdk / "generated_samples"
540
+ out_path = self._generated_tests_samples_folder("generated_samples")
524
541
  for client in self.code_model.clients:
525
542
  for op_group in client.operation_groups:
526
543
  for operation in op_group.operations:
@@ -549,7 +566,7 @@ class JinjaSerializer(ReaderAndWriter):
549
566
 
550
567
  def _serialize_and_write_test(self, env: Environment):
551
568
  self.code_model.for_test = True
552
- out_path = self._root_of_sdk / "generated_tests"
569
+ out_path = self._generated_tests_samples_folder("generated_tests")
553
570
  general_serializer = TestGeneralSerializer(code_model=self.code_model, env=env)
554
571
  self.write_file(out_path / "conftest.py", general_serializer.serialize_conftest())
555
572
  if not self.code_model.options["azure-arm"]:
@@ -1053,10 +1053,13 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
1053
1053
  f" error = _failsafe_deserialize({type_annotation},{pylint_disable}\n response)"
1054
1054
  )
1055
1055
  else:
1056
- retval.append(
1057
- " error = self._deserialize.failsafe_deserialize("
1058
- f"{type_annotation},{pylint_disable}\n "
1059
- "pipeline_response)"
1056
+ retval.extend(
1057
+ [
1058
+ " error = self._deserialize.failsafe_deserialize(",
1059
+ f" {type_annotation},{pylint_disable}",
1060
+ " pipeline_response,",
1061
+ " )",
1062
+ ]
1060
1063
  )
1061
1064
  # add build-in error type
1062
1065
  # TODO: we should decide whether need to this wrapper for customized error type
@@ -1097,10 +1100,13 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
1097
1100
  f"{type_annotation},{pylint_disable}\n response)"
1098
1101
  )
1099
1102
  else:
1100
- retval.append(
1101
- " error = self._deserialize.failsafe_deserialize("
1102
- f"{type_annotation},{pylint_disable}\n "
1103
- "pipeline_response)"
1103
+ retval.extend(
1104
+ [
1105
+ " error = self._deserialize.failsafe_deserialize(",
1106
+ f" {type_annotation},{pylint_disable}",
1107
+ " pipeline_response,",
1108
+ " )",
1109
+ ]
1104
1110
  )
1105
1111
  condition = "elif"
1106
1112
  # default error handling
@@ -1111,11 +1117,22 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
1111
1117
  if builder.non_default_errors:
1112
1118
  retval.append(" else:")
1113
1119
  if self.code_model.options["models-mode"] == "dpg":
1114
- retval.append(f"{indent}error = _failsafe_deserialize({default_error_deserialization}, response)")
1120
+ retval.extend(
1121
+ [
1122
+ f"{indent}error = _failsafe_deserialize(",
1123
+ f"{indent} {default_error_deserialization}",
1124
+ f"{indent} response,",
1125
+ f"{indent})",
1126
+ ]
1127
+ )
1115
1128
  else:
1116
- retval.append(
1117
- f"{indent}error = self._deserialize.failsafe_deserialize({default_error_deserialization}, "
1118
- "pipeline_response)"
1129
+ retval.extend(
1130
+ [
1131
+ f"{indent}error = self._deserialize.failsafe_deserialize(",
1132
+ f"{indent} {default_error_deserialization}",
1133
+ f"{indent} pipeline_response,",
1134
+ f"{indent})",
1135
+ ]
1119
1136
  )
1120
1137
  retval.append(
1121
1138
  " raise HttpResponseError(response=response{}{})".format(
@@ -202,9 +202,11 @@ class ParameterSerializer:
202
202
  if is_content_type_optional and not type_annotation.startswith("Optional[")
203
203
  else type_annotation
204
204
  )
205
- if kwarg.client_default_value is not None or kwarg.optional:
205
+ if kwarg.client_default_value is not None or kwarg.optional or kwarg.constant:
206
206
  if check_client_input and kwarg.check_client_input:
207
207
  default_value = f"self._config.{kwarg.client_name}"
208
+ elif kwarg.constant:
209
+ default_value = kwarg.type.get_declaration(None)
208
210
  else:
209
211
  default_value = kwarg.client_default_value_declaration
210
212
  if check_kwarg_dict and (kwarg.location in [ParameterLocation.HEADER, ParameterLocation.QUERY]):
@@ -1,21 +1,39 @@
1
1
  {% macro wrap_model_string(doc_string, wrap_string, suffix_string="") %}
2
- {%- set lines = doc_string.split('\n') -%}
3
- {%- set processed_lines = [] -%}
4
- {%- for line in lines -%}
5
- {%- set stripped = line.strip() -%}
6
- {%- if stripped.startswith('* ') -%}
7
- {%- set bullet_with_indent = ' ' + stripped -%}
8
- {%- set wrapped = bullet_with_indent | wordwrap(width=95, break_long_words=False, break_on_hyphens=False, wrapstring=wrap_string + ' ') -%}
9
- {%- set _ = processed_lines.append(wrapped) -%}
10
- {%- elif stripped -%}
11
- {%- set wrapped = line | wordwrap(width=95, break_long_words=False, break_on_hyphens=False, wrapstring=wrap_string) -%}
12
- {%- set _ = processed_lines.append(wrapped) -%}
13
- {%- endif -%}
14
- {%- endfor -%}
15
- {%- set original_result = processed_lines | join('\n') -%}
2
+ {# Check if this is a sphinx documentation line that should not have extra prefix spacing #}
3
+ {%- set is_sphinx_doc = doc_string.strip().startswith(':ivar') or doc_string.strip().startswith(':vartype') or doc_string.strip().startswith(':param') or doc_string.strip().startswith(':type') -%}
4
+ {# Custom handling for bullet points - normalization is now done in preprocessing #}
5
+ {% set enable_custom_handling = "\n* " in doc_string or doc_string.startswith("* ") %}
6
+ {%- if enable_custom_handling -%}
7
+ {%- set lines = doc_string.split('\n') -%}
8
+ {%- set result_lines = [] -%}
9
+ {%- for line in lines -%}
10
+ {%- if line.startswith('* ') -%}
11
+ {# Handle bullet points with proper continuation alignment #}
12
+ {%- set bullet_content = line[2:] -%}
13
+ {%- set base_indent = wrap_string.lstrip('\n') -%}
14
+ {%- set bullet_line = base_indent + ' * ' + bullet_content -%}
15
+ {%- set continuation_spaces = base_indent + ' ' -%}
16
+ {%- set wrapped = bullet_line | wordwrap(width=95, break_long_words=False, break_on_hyphens=False, wrapstring='\n' + continuation_spaces) -%}
17
+ {%- set _ = result_lines.append(wrapped) -%}
18
+ {%- elif line.strip() -%}
19
+ {%- set wrapped = line.strip() | wordwrap(width=95, break_long_words=False, break_on_hyphens=False, wrapstring=wrap_string) -%}
20
+ {%- set _ = result_lines.append(wrapped) -%}
21
+ {%- else -%}
22
+ {%- set _ = result_lines.append('') -%}
23
+ {%- endif -%}
24
+ {%- endfor -%}
25
+ {%- set original_result = result_lines | join('\n') -%}
26
+ {%- else -%}
27
+ {# Regular text handling #}
28
+ {%- set original_result = doc_string | wordwrap(width=95, break_long_words=False, break_on_hyphens=False, wrapstring=wrap_string) -%}
29
+ {%- endif -%}
16
30
  {% set list_result = original_result.split('\n') %}
17
31
  {% for line in list_result %}
18
- {% set prefix = "" if loop.index == 1 else " " %}
32
+ {%- if is_sphinx_doc and enable_custom_handling -%}
33
+ {%- set prefix = "" -%}
34
+ {%- else -%}
35
+ {%- set prefix = "" if loop.index == 1 else " " -%}
36
+ {%- endif -%}
19
37
  {% set suffix = suffix_string if list_result | length == loop.index %}
20
38
  {{ prefix }}{{ line }}{{ suffix }}
21
39
  {% endfor %}
@@ -78,7 +78,7 @@
78
78
  if key in self.__flattened_items:
79
79
  if self.{{ flattened_property_attr }} is None:
80
80
  self.{{ flattened_property_attr }} = self._attr_to_rest_field["{{ flattened_property_attr }}"]._class_type()
81
- setattr(self.properties, key, value)
81
+ setattr(self.{{ flattened_property_attr }}, key, value)
82
82
  else:
83
83
  super().__setattr__(key, value)
84
84
  {% endif %}
@@ -1,18 +1,4 @@
1
- {% macro wrap_string(string, wrapstring, width=95) %}
2
- {%- set lines = string.split('\n') -%}
3
- {%- set processed_lines = [] -%}
4
- {%- for line in lines -%}
5
- {%- set stripped = line.strip() -%}
6
- {%- if stripped.startswith('* ') -%}
7
- {%- set bullet_with_indent = stripped -%}
8
- {%- set wrapped = bullet_with_indent | wordwrap(width=width, break_long_words=False, break_on_hyphens=False, wrapstring=wrapstring + ' ') -%}
9
- {%- set _ = processed_lines.append(wrapped) -%}
10
- {%- elif stripped -%}
11
- {%- set wrapped = line | wordwrap(width=width, break_long_words=False, break_on_hyphens=False, wrapstring=wrapstring) -%}
12
- {%- set _ = processed_lines.append(wrapped) -%}
13
- {%- endif -%}
14
- {%- endfor -%}
15
- {{ processed_lines | join('\n') | replace("\\", "\\\\") }}{%- endmacro %}
1
+ {% macro wrap_string(string, wrapstring, width=95) %}{{ string | replace("\\", "\\\\") | wordwrap(width=width, break_long_words=False, break_on_hyphens=False, wrapstring=wrapstring)}}{% endmacro %}
16
2
 
17
3
  {% macro description(builder, serializer) %}
18
4
  {% set example_template = serializer.example_template(builder) %}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autorest/python",
3
- "version": "6.41.3",
3
+ "version": "6.42.0",
4
4
  "description": "The Python extension for generators in AutoRest.",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -19,7 +19,7 @@
19
19
  },
20
20
  "homepage": "https://github.com/Azure/autorest.python/blob/main/README.md",
21
21
  "dependencies": {
22
- "@typespec/http-client-python": "~0.19.1",
22
+ "@typespec/http-client-python": "~0.20.0",
23
23
  "@autorest/system-requirements": "~1.0.2",
24
24
  "fs-extra": "~11.2.0",
25
25
  "tsx": "~4.19.1"