@typespec/http-client-python 0.31.1 → 0.33.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 (59) hide show
  1. package/dist/emitter/emitter.d.ts.map +1 -1
  2. package/dist/emitter/emitter.js +20 -169
  3. package/dist/emitter/emitter.js.map +1 -1
  4. package/dist/emitter/lib.d.ts +6 -0
  5. package/dist/emitter/lib.d.ts.map +1 -1
  6. package/dist/emitter/lib.js +29 -0
  7. package/dist/emitter/lib.js.map +1 -1
  8. package/dist/emitter/node-runner.browser.d.ts +12 -0
  9. package/dist/emitter/node-runner.browser.d.ts.map +1 -0
  10. package/dist/emitter/node-runner.browser.js +16 -0
  11. package/dist/emitter/node-runner.browser.js.map +1 -0
  12. package/dist/emitter/node-runner.d.ts +12 -0
  13. package/dist/emitter/node-runner.d.ts.map +1 -0
  14. package/dist/emitter/node-runner.js +180 -0
  15. package/dist/emitter/node-runner.js.map +1 -0
  16. package/dist/emitter/pyodide-loader.browser.d.ts +12 -0
  17. package/dist/emitter/pyodide-loader.browser.d.ts.map +1 -0
  18. package/dist/emitter/pyodide-loader.browser.js +33 -0
  19. package/dist/emitter/pyodide-loader.browser.js.map +1 -0
  20. package/dist/emitter/pyodide-loader.d.ts +3 -0
  21. package/dist/emitter/pyodide-loader.d.ts.map +1 -0
  22. package/dist/emitter/pyodide-loader.js +6 -0
  23. package/dist/emitter/pyodide-loader.js.map +1 -0
  24. package/dist/emitter/types.d.ts.map +1 -1
  25. package/dist/emitter/types.js +7 -0
  26. package/dist/emitter/types.js.map +1 -1
  27. package/emitter/src/emitter.ts +18 -189
  28. package/emitter/src/lib.ts +36 -0
  29. package/emitter/src/node-runner.browser.ts +33 -0
  30. package/emitter/src/node-runner.ts +220 -0
  31. package/emitter/src/pyodide-loader.browser.ts +49 -0
  32. package/emitter/src/pyodide-loader.ts +6 -0
  33. package/emitter/src/types.ts +7 -0
  34. package/emitter/temp/tsconfig.tsbuildinfo +1 -1
  35. package/eng/scripts/setup/__pycache__/package_manager.cpython-311.pyc +0 -0
  36. package/eng/scripts/setup/__pycache__/venvtools.cpython-311.pyc +0 -0
  37. package/generator/build/lib/pygen/__init__.py +1 -0
  38. package/generator/build/lib/pygen/codegen/models/primitive_types.py +15 -1
  39. package/generator/build/lib/pygen/codegen/models/utils.py +20 -5
  40. package/generator/build/lib/pygen/codegen/serializers/builder_serializer.py +9 -2
  41. package/generator/build/lib/pygen/codegen/serializers/general_serializer.py +32 -2
  42. package/generator/build/lib/pygen/codegen/templates/model_base.py.jinja2 +34 -1
  43. package/generator/build/lib/pygen/codegen/templates/packaging_templates/pyproject.toml.jinja2 +30 -2
  44. package/generator/build/lib/pygen/codegen/templates/serialization.py.jinja2 +109 -0
  45. package/generator/build/lib/pygen/preprocess/__init__.py +168 -52
  46. package/generator/build/lib/pygen/utils.py +38 -4
  47. package/generator/component-detection-pip-report.json +2 -2
  48. package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
  49. package/generator/pygen/__init__.py +1 -0
  50. package/generator/pygen/codegen/models/primitive_types.py +15 -1
  51. package/generator/pygen/codegen/models/utils.py +20 -5
  52. package/generator/pygen/codegen/serializers/builder_serializer.py +9 -2
  53. package/generator/pygen/codegen/serializers/general_serializer.py +32 -2
  54. package/generator/pygen/codegen/templates/model_base.py.jinja2 +34 -1
  55. package/generator/pygen/codegen/templates/packaging_templates/pyproject.toml.jinja2 +30 -2
  56. package/generator/pygen/codegen/templates/serialization.py.jinja2 +109 -0
  57. package/generator/pygen/preprocess/__init__.py +168 -52
  58. package/generator/pygen/utils.py +38 -4
  59. package/package.json +12 -8
@@ -66,7 +66,26 @@ class GeneralSerializer(BaseSerializer):
66
66
  if dep_version > default_version:
67
67
  version_map[dep_name] = str(dep_version)
68
68
 
69
- def external_lib_version_map(self, file_content: str, additional_version_map: dict[str, str]) -> dict:
69
+ # Project-level pyproject.toml fields that may be preserved across regeneration
70
+ # via the "keep-pyproject-fields" option.
71
+ KEEPABLE_PROJECT_FIELDS = ("authors", "description", "classifiers", "urls")
72
+
73
+ @staticmethod
74
+ def _parse_keep_pyproject_fields(value: Any) -> tuple[str, ...]:
75
+ # The "keep-pyproject-fields" option may be a comma-separated string (e.g.
76
+ # "authors,description") or an already-parsed sequence of field names.
77
+ if not value:
78
+ return ()
79
+ if isinstance(value, str):
80
+ return tuple(field.strip() for field in value.split(",") if field.strip())
81
+ return tuple(value)
82
+
83
+ def external_lib_version_map(
84
+ self,
85
+ file_content: str,
86
+ additional_version_map: dict[str, str],
87
+ keep_pyproject_fields: tuple[str, ...] = (),
88
+ ) -> dict:
70
89
  # Load the pyproject.toml file if it exists and extract fields to keep.
71
90
  result: dict = {"KEEP_FIELDS": {}}
72
91
  try:
@@ -85,6 +104,14 @@ class GeneralSerializer(BaseSerializer):
85
104
 
86
105
  # Process dependencies
87
106
  if "project" in loaded_pyproject_toml:
107
+ project = loaded_pyproject_toml["project"]
108
+
109
+ # Keep manually customized project fields the emitter would otherwise overwrite.
110
+ # Only the fields explicitly listed in the "keep-pyproject-fields" option are preserved.
111
+ for field in keep_pyproject_fields:
112
+ if field in self.KEEPABLE_PROJECT_FIELDS and field in project:
113
+ result["KEEP_FIELDS"][f"project.{field}"] = project[field]
114
+
88
115
  # Handle main dependencies
89
116
  if "dependencies" in loaded_pyproject_toml["project"]:
90
117
  kept_deps = []
@@ -127,7 +154,10 @@ class GeneralSerializer(BaseSerializer):
127
154
 
128
155
  # Add fields to keep from an existing pyproject.toml
129
156
  if template_name == "pyproject.toml.jinja2":
130
- params = self.external_lib_version_map(file_content, additional_version_map)
157
+ keep_pyproject_fields = self._parse_keep_pyproject_fields(
158
+ self.code_model.options.get("keep-pyproject-fields")
159
+ )
160
+ params = self.external_lib_version_map(file_content, additional_version_map, keep_pyproject_fields)
131
161
  else:
132
162
  params = {}
133
163
 
@@ -112,6 +112,29 @@ def _serialize_bytes(o, format: typing.Optional[str] = None) -> str:
112
112
  return encoded
113
113
 
114
114
 
115
+ def _serialize_duration(td: timedelta, format: typing.Optional[str] = None):
116
+ """Serialize a timedelta to its wire representation.
117
+
118
+ For the ``seconds``/``milliseconds`` encodings the value is converted to a
119
+ numeric value, otherwise it falls back to an ISO 8601 duration string.
120
+
121
+ :param timedelta td: The timedelta to serialize.
122
+ :param str format: The duration encoding format.
123
+ :rtype: int or float or str
124
+ :return: serialized duration
125
+ """
126
+ seconds = td.total_seconds()
127
+ if format == "duration-seconds-int":
128
+ return int(seconds)
129
+ if format == "duration-seconds-float":
130
+ return seconds
131
+ if format == "duration-milliseconds-int":
132
+ return int(seconds * 1000)
133
+ if format == "duration-milliseconds-float":
134
+ return seconds * 1000
135
+ return _timedelta_as_isostr(td)
136
+
137
+
115
138
  def _serialize_datetime(o, format: typing.Optional[str] = None):
116
139
  if hasattr(o, "year") and hasattr(o, "hour"):
117
140
  if format == "rfc7231":
@@ -307,6 +330,12 @@ def _deserialize_duration(attr):
307
330
  return isodate.parse_duration(attr)
308
331
 
309
332
 
333
+ def _deserialize_duration_numeric(attr, unit):
334
+ if isinstance(attr, timedelta):
335
+ return attr
336
+ return timedelta(**{unit: float(attr)})
337
+
338
+
310
339
  def _deserialize_decimal(attr):
311
340
  if isinstance(attr, decimal.Decimal):
312
341
  return attr
@@ -336,6 +365,10 @@ _DESERIALIZE_MAPPING_WITHFORMAT = {
336
365
  "unix-timestamp": _deserialize_datetime_unix_timestamp,
337
366
  "base64": _deserialize_bytes,
338
367
  "base64url": _deserialize_bytes_base64,
368
+ "duration-seconds-int": functools.partial(_deserialize_duration_numeric, unit="seconds"),
369
+ "duration-seconds-float": functools.partial(_deserialize_duration_numeric, unit="seconds"),
370
+ "duration-milliseconds-int": functools.partial(_deserialize_duration_numeric, unit="milliseconds"),
371
+ "duration-milliseconds-float": functools.partial(_deserialize_duration_numeric, unit="milliseconds"),
339
372
  }
340
373
 
341
374
 
@@ -576,7 +609,7 @@ def _serialize(o, format: typing.Optional[str] = None): # pylint: disable=too-m
576
609
  pass
577
610
  # Last, try datetime.timedelta
578
611
  try:
579
- return _timedelta_as_isostr(o)
612
+ return _serialize_duration(o, format)
580
613
  except AttributeError:
581
614
  # This will be raised when it hits value.total_seconds in the method above
582
615
  pass
@@ -11,15 +11,32 @@ build-backend = "setuptools.build_meta"
11
11
  [project]
12
12
  name = "{{ options.get('package-name')|lower }}"
13
13
  {% if options.get('package-mode') %}
14
+ {% if KEEP_FIELDS and KEEP_FIELDS.get('project.authors') %}
15
+ authors = [
16
+ {% for author in KEEP_FIELDS.get('project.authors') %}
17
+ { {% for key, val in author.items() %}{{ key }} = {{ val|tojson }}{% if not loop.last %}, {% endif %}{% endfor %} },
18
+ {% endfor %}
19
+ ]
20
+ {% else %}
14
21
  authors = [
15
22
  { name = "{{ code_model.company_name }}"{% if code_model.is_azure_flavor %}, email = "azpysdkhelp@microsoft.com"{% endif %} },
16
23
  ]
17
- {% if options.get("azure-arm") %}
24
+ {% endif %}
25
+ {% if KEEP_FIELDS and KEEP_FIELDS.get('project.description') %}
26
+ description = {{ KEEP_FIELDS.get('project.description')|tojson }}
27
+ {% elif options.get("azure-arm") %}
18
28
  description = "Microsoft Azure {{ options.get('package-pprint-name') }} Client Library for Python"
19
29
  {% else %}
20
30
  description = "{{ code_model.company_name }} {% if code_model.is_azure_flavor and not options.get('package-pprint-name').startswith('Azure ') %}Azure {% endif %}{{ options.get('package-pprint-name') }} Client Library for Python"
21
31
  {% endif %}
22
32
  license = "MIT"
33
+ {% if KEEP_FIELDS and KEEP_FIELDS.get('project.classifiers') %}
34
+ classifiers = [
35
+ {% for classifier in KEEP_FIELDS.get('project.classifiers') %}
36
+ {{ classifier|tojson }},
37
+ {% endfor %}
38
+ ]
39
+ {% else %}
23
40
  classifiers = [
24
41
  "Development Status :: {{ dev_status }}",
25
42
  "Programming Language :: Python",
@@ -29,10 +46,15 @@ classifiers = [
29
46
  "Programming Language :: Python :: 3.{{ version }}",
30
47
  {% endfor %}
31
48
  ]
49
+ {% endif %}
32
50
  requires-python = ">={{ MIN_PYTHON_VERSION }}"
33
51
  {% else %}
52
+ {% if KEEP_FIELDS and KEEP_FIELDS.get('project.description') %}
53
+ description = {{ KEEP_FIELDS.get('project.description')|tojson }}
54
+ {% else %}
34
55
  description = "{{ options.get('package-name') }}"
35
56
  {% endif %}
57
+ {% endif %}
36
58
  {% if code_model.is_azure_flavor %}
37
59
  keywords = ["azure", "azure sdk"]
38
60
  {% endif %}
@@ -77,7 +99,13 @@ version = "{{ options.get("package-version", "unknown") }}"
77
99
  ]
78
100
  {% endfor %}
79
101
  {% endif %}
80
- {% if code_model.is_azure_flavor %}
102
+ {% if KEEP_FIELDS and KEEP_FIELDS.get('project.urls') %}
103
+
104
+ [project.urls]
105
+ {% for key, val in KEEP_FIELDS.get('project.urls').items() %}
106
+ {{ key }} = {{ val|tojson }}
107
+ {% endfor %}
108
+ {% elif code_model.is_azure_flavor %}
81
109
 
82
110
  [project.urls]
83
111
  repository = "https://github.com/Azure/azure-sdk-for-python"
@@ -516,6 +516,10 @@ class Serializer: # pylint: disable=too-many-public-methods
516
516
  "rfc-1123": Serializer.serialize_rfc,
517
517
  "unix-time": Serializer.serialize_unix,
518
518
  "duration": Serializer.serialize_duration,
519
+ "duration-seconds-int": Serializer.serialize_duration_seconds_int,
520
+ "duration-seconds-float": Serializer.serialize_duration_seconds_float,
521
+ "duration-milliseconds-int": Serializer.serialize_duration_milliseconds_int,
522
+ "duration-milliseconds-float": Serializer.serialize_duration_milliseconds_float,
519
523
  "date": Serializer.serialize_date,
520
524
  "time": Serializer.serialize_time,
521
525
  "decimal": Serializer.serialize_decimal,
@@ -1105,6 +1109,61 @@ class Serializer: # pylint: disable=too-many-public-methods
1105
1109
  attr = isodate.parse_duration(attr)
1106
1110
  return isodate.duration_isoformat(attr)
1107
1111
 
1112
+ @staticmethod
1113
+ def _serialize_duration_numeric(attr, scale, as_int):
1114
+ """Serialize a TimeDelta into a numeric value scaled to the wire unit.
1115
+
1116
+ :param TimeDelta attr: Object to be serialized.
1117
+ :param int scale: Multiplier applied to total seconds (1 for seconds, 1000 for milliseconds).
1118
+ :param bool as_int: Whether to truncate the result to an int.
1119
+ :rtype: int or float
1120
+ :return: serialized duration
1121
+ """
1122
+ if isinstance(attr, str):
1123
+ attr = isodate.parse_duration(attr)
1124
+ value = attr.total_seconds() * scale if isinstance(attr, datetime.timedelta) else attr
1125
+ return int(value) if as_int else float(value)
1126
+
1127
+ @staticmethod
1128
+ def serialize_duration_seconds_int(attr, **kwargs): # pylint: disable=unused-argument
1129
+ """Serialize TimeDelta object into an integer number of seconds.
1130
+
1131
+ :param TimeDelta attr: Object to be serialized.
1132
+ :rtype: int
1133
+ :return: serialized duration
1134
+ """
1135
+ return Serializer._serialize_duration_numeric(attr, 1, True)
1136
+
1137
+ @staticmethod
1138
+ def serialize_duration_seconds_float(attr, **kwargs): # pylint: disable=unused-argument
1139
+ """Serialize TimeDelta object into a floating point number of seconds.
1140
+
1141
+ :param TimeDelta attr: Object to be serialized.
1142
+ :rtype: float
1143
+ :return: serialized duration
1144
+ """
1145
+ return Serializer._serialize_duration_numeric(attr, 1, False)
1146
+
1147
+ @staticmethod
1148
+ def serialize_duration_milliseconds_int(attr, **kwargs): # pylint: disable=unused-argument
1149
+ """Serialize TimeDelta object into an integer number of milliseconds.
1150
+
1151
+ :param TimeDelta attr: Object to be serialized.
1152
+ :rtype: int
1153
+ :return: serialized duration
1154
+ """
1155
+ return Serializer._serialize_duration_numeric(attr, 1000, True)
1156
+
1157
+ @staticmethod
1158
+ def serialize_duration_milliseconds_float(attr, **kwargs): # pylint: disable=unused-argument
1159
+ """Serialize TimeDelta object into a floating point number of milliseconds.
1160
+
1161
+ :param TimeDelta attr: Object to be serialized.
1162
+ :rtype: float
1163
+ :return: serialized duration
1164
+ """
1165
+ return Serializer._serialize_duration_numeric(attr, 1000, False)
1166
+
1108
1167
  @staticmethod
1109
1168
  def serialize_rfc(attr, **kwargs): # pylint: disable=unused-argument
1110
1169
  """Serialize Datetime object into RFC-1123 formatted string.
@@ -1377,6 +1436,10 @@ class Deserializer:
1377
1436
  "rfc-1123": Deserializer.deserialize_rfc,
1378
1437
  "unix-time": Deserializer.deserialize_unix,
1379
1438
  "duration": Deserializer.deserialize_duration,
1439
+ "duration-seconds-int": Deserializer.deserialize_duration_seconds,
1440
+ "duration-seconds-float": Deserializer.deserialize_duration_seconds,
1441
+ "duration-milliseconds-int": Deserializer.deserialize_duration_milliseconds,
1442
+ "duration-milliseconds-float": Deserializer.deserialize_duration_milliseconds,
1380
1443
  "date": Deserializer.deserialize_date,
1381
1444
  "time": Deserializer.deserialize_time,
1382
1445
  "decimal": Deserializer.deserialize_decimal,
@@ -1389,6 +1452,10 @@ class Deserializer:
1389
1452
  }
1390
1453
  self.deserialize_expected_types = {
1391
1454
  "duration": (isodate.Duration, datetime.timedelta),
1455
+ "duration-seconds-int": (isodate.Duration, datetime.timedelta),
1456
+ "duration-seconds-float": (isodate.Duration, datetime.timedelta),
1457
+ "duration-milliseconds-int": (isodate.Duration, datetime.timedelta),
1458
+ "duration-milliseconds-float": (isodate.Duration, datetime.timedelta),
1392
1459
  "iso-8601": (datetime.datetime),
1393
1460
  }
1394
1461
  self.dependencies: dict[str, type] = dict(classes) if classes else {}
@@ -1950,6 +2017,48 @@ class Deserializer:
1950
2017
  raise DeserializationError(msg) from err
1951
2018
  return duration
1952
2019
 
2020
+ @staticmethod
2021
+ def _deserialize_duration_numeric(attr, unit):
2022
+ """Deserialize a numeric duration value into a TimeDelta object.
2023
+
2024
+ :param float attr: response value to be deserialized.
2025
+ :param str unit: The wire unit, used as the ``timedelta`` keyword
2026
+ (``"seconds"`` or ``"milliseconds"``).
2027
+ :return: Deserialized duration
2028
+ :rtype: TimeDelta
2029
+ :raises DeserializationError: if value is invalid.
2030
+ """
2031
+ if isinstance(attr, ET.Element):
2032
+ attr = attr.text
2033
+ try:
2034
+ duration = datetime.timedelta(**{unit: float(attr)}) # type: ignore
2035
+ except (ValueError, OverflowError, TypeError) as err:
2036
+ msg = "Cannot deserialize duration object."
2037
+ raise DeserializationError(msg) from err
2038
+ return duration
2039
+
2040
+ @staticmethod
2041
+ def deserialize_duration_seconds(attr):
2042
+ """Deserialize a numeric number of seconds into a TimeDelta object.
2043
+
2044
+ :param float attr: response value to be deserialized.
2045
+ :return: Deserialized duration
2046
+ :rtype: TimeDelta
2047
+ :raises DeserializationError: if value is invalid.
2048
+ """
2049
+ return Deserializer._deserialize_duration_numeric(attr, "seconds")
2050
+
2051
+ @staticmethod
2052
+ def deserialize_duration_milliseconds(attr):
2053
+ """Deserialize a numeric number of milliseconds into a TimeDelta object.
2054
+
2055
+ :param float attr: response value to be deserialized.
2056
+ :return: Deserialized duration
2057
+ :rtype: TimeDelta
2058
+ :raises DeserializationError: if value is invalid.
2059
+ """
2060
+ return Deserializer._deserialize_duration_numeric(attr, "milliseconds")
2061
+
1953
2062
  @staticmethod
1954
2063
  def deserialize_date(attr):
1955
2064
  """Deserialize ISO-8601 formatted string into Date object.