@typespec/http-client-python 0.22.0 → 0.23.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 (25) hide show
  1. package/dist/emitter/types.d.ts.map +1 -1
  2. package/dist/emitter/types.js +1 -0
  3. package/dist/emitter/types.js.map +1 -1
  4. package/emitter/src/types.ts +1 -0
  5. package/emitter/temp/tsconfig.tsbuildinfo +1 -1
  6. package/eng/scripts/ci/regenerate.ts +5 -1
  7. package/eng/scripts/setup/__pycache__/package_manager.cpython-311.pyc +0 -0
  8. package/eng/scripts/setup/__pycache__/venvtools.cpython-311.pyc +0 -0
  9. package/generator/build/lib/pygen/codegen/models/property.py +1 -0
  10. package/generator/build/lib/pygen/codegen/serializers/model_serializer.py +2 -0
  11. package/generator/build/lib/pygen/codegen/templates/macros.jinja2 +12 -5
  12. package/generator/build/lib/pygen/codegen/templates/model_base.py.jinja2 +28 -0
  13. package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
  14. package/generator/pygen/codegen/models/property.py +1 -0
  15. package/generator/pygen/codegen/serializers/model_serializer.py +2 -0
  16. package/generator/pygen/codegen/templates/macros.jinja2 +12 -5
  17. package/generator/pygen/codegen/templates/model_base.py.jinja2 +28 -0
  18. package/generator/test/azure/requirements.txt +1 -0
  19. package/generator/test/generic_mock_api_tests/asynctests/test_encode_array_async.py +43 -0
  20. package/generator/test/generic_mock_api_tests/asynctests/test_specs_documentation_async.py +60 -0
  21. package/generator/test/generic_mock_api_tests/test_encode_array.py +38 -0
  22. package/generator/test/generic_mock_api_tests/test_specs_documentation.py +52 -0
  23. package/generator/test/unbranded/requirements.txt +1 -0
  24. package/generator/test/unittests/test_model_base_serialization.py +264 -0
  25. package/package.json +1 -1
@@ -22,7 +22,7 @@ const argv = parseArgs({
22
22
  });
23
23
 
24
24
  // Add this near the top with other constants
25
- const SKIP_SPECS = ["type/union/discriminated", "documentation"];
25
+ const SKIP_SPECS = ["type/union/discriminated"];
26
26
 
27
27
  // Get the directory of the current file
28
28
  const PLUGIN_DIR = argv.values.pluginDir
@@ -272,6 +272,10 @@ const EMITTER_OPTIONS: Record<string, Record<string, string> | Record<string, st
272
272
  "package-name": "typetest-union",
273
273
  namespace: "typetest.union",
274
274
  },
275
+ documentation: {
276
+ "package-name": "specs-documentation",
277
+ namespace: "specs.documentation",
278
+ },
275
279
  };
276
280
 
277
281
  function toPosix(dir: string): string {
@@ -40,6 +40,7 @@ class Property(BaseModel): # pylint: disable=too-many-instance-attributes
40
40
  self.is_multipart_file_input: bool = yaml_data.get("isMultipartFileInput", False)
41
41
  self.flatten = self.yaml_data.get("flatten", False) and not getattr(self.type, "flattened_property", False)
42
42
  self.original_tsp_name: Optional[str] = self.yaml_data.get("originalTspName")
43
+ self.encode: Optional[str] = self.yaml_data.get("encode")
43
44
 
44
45
  def pylint_disable(self) -> str:
45
46
  retval: str = ""
@@ -329,6 +329,8 @@ class DpgModelSerializer(_ModelSerializer):
329
329
  args.append("is_multipart_file_input=True")
330
330
  elif hasattr(prop.type, "encode") and prop.type.encode: # type: ignore
331
331
  args.append(f'format="{prop.type.encode}"') # type: ignore
332
+ elif prop.encode:
333
+ args.append(f'format="{prop.encode}"')
332
334
 
333
335
  if prop.xml_metadata:
334
336
  args.append(f"xml={prop.xml_metadata}")
@@ -5,21 +5,28 @@
5
5
  {% set enable_custom_handling = "\n* " in doc_string or doc_string.startswith("* ") %}
6
6
  {%- if enable_custom_handling -%}
7
7
  {%- set lines = doc_string.split('\n') -%}
8
+ {%- set base_indent = wrap_string.lstrip('\n') -%}
8
9
  {%- set result_lines = [] -%}
9
10
  {%- for line in lines -%}
10
11
  {%- if line.startswith('* ') -%}
11
12
  {# Handle bullet points with proper continuation alignment #}
12
13
  {%- set bullet_content = line[2:] -%}
13
- {%- set base_indent = wrap_string.lstrip('\n') -%}
14
14
  {%- set bullet_line = base_indent + ' * ' + bullet_content -%}
15
15
  {%- set continuation_spaces = base_indent + ' ' -%}
16
16
  {%- set wrapped = bullet_line | wordwrap(width=95, break_long_words=False, break_on_hyphens=False, wrapstring='\n' + continuation_spaces) -%}
17
17
  {%- set _ = result_lines.append(wrapped) -%}
18
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) -%}
19
+ {%- set line_indent = '' if line.strip().startswith(':') or loop.index == 1 else (base_indent + ' ') -%}
20
+ {%- set wrapped = (line_indent + line) | wordwrap(width=95, break_long_words=False, break_on_hyphens=False, wrapstring=wrap_string) -%}
21
+ {%- for line in wrapped.split('\n') -%}
22
+ {%- set prefix = "" if loop.index == 1 else " " -%}
23
+ {%- set _ = result_lines.append(prefix + line) -%}
24
+ {%- endfor -%}
21
25
  {%- else -%}
22
- {%- set _ = result_lines.append('') -%}
26
+ {# Do not add continuous blank lines #}
27
+ {%- if (result_lines and result_lines[-1] != '') or not result_lines -%}
28
+ {%- set _ = result_lines.append('') -%}
29
+ {%- endif -%}
23
30
  {%- endif -%}
24
31
  {%- endfor -%}
25
32
  {%- set original_result = result_lines | join('\n') -%}
@@ -37,4 +44,4 @@
37
44
  {% set suffix = suffix_string if list_result | length == loop.index %}
38
45
  {{ prefix }}{{ line }}{{ suffix }}
39
46
  {% endfor %}
40
- {% endmacro %}
47
+ {% endmacro %}
@@ -179,6 +179,19 @@ _VALID_RFC7231 = re.compile(
179
179
  r"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s\d{4}\s\d{2}:\d{2}:\d{2}\sGMT"
180
180
  )
181
181
 
182
+ _ARRAY_ENCODE_MAPPING = {
183
+ "pipeDelimited": "|",
184
+ "spaceDelimited": " ",
185
+ "commaDelimited": ",",
186
+ "newlineDelimited": "\n",
187
+ }
188
+
189
+ def _deserialize_array_encoded(delimit: str, attr):
190
+ if isinstance(attr, str):
191
+ if attr == "":
192
+ return []
193
+ return attr.split(delimit)
194
+ return attr
182
195
 
183
196
  def _deserialize_datetime(attr: typing.Union[str, datetime]) -> datetime:
184
197
  """Deserialize ISO-8601 formatted string into Datetime object.
@@ -323,6 +336,8 @@ _DESERIALIZE_MAPPING_WITHFORMAT = {
323
336
  def get_deserializer(annotation: typing.Any, rf: typing.Optional["_RestField"] = None):
324
337
  if annotation is int and rf and rf._format == "str":
325
338
  return _deserialize_int_as_str
339
+ if annotation is str and rf and rf._format in _ARRAY_ENCODE_MAPPING:
340
+ return functools.partial(_deserialize_array_encoded, _ARRAY_ENCODE_MAPPING[rf._format])
326
341
  if rf and rf._format:
327
342
  return _DESERIALIZE_MAPPING_WITHFORMAT.get(rf._format)
328
343
  {% if code_model.has_external_type %}
@@ -497,6 +512,8 @@ def _is_model(obj: typing.Any) -> bool:
497
512
 
498
513
  def _serialize(o, format: typing.Optional[str] = None): # pylint: disable=too-many-return-statements
499
514
  if isinstance(o, list):
515
+ if format in _ARRAY_ENCODE_MAPPING and all(isinstance(x, str) for x in o):
516
+ return _ARRAY_ENCODE_MAPPING[format].join(o)
500
517
  return [_serialize(x, format) for x in o]
501
518
  if isinstance(o, dict):
502
519
  return {k: _serialize(v, format) for k, v in o.items()}
@@ -809,6 +826,17 @@ def _deserialize_sequence(
809
826
  return obj
810
827
  if isinstance(obj, ET.Element):
811
828
  obj = list(obj)
829
+ try:
830
+ if (
831
+ isinstance(obj, str)
832
+ and isinstance(deserializer, functools.partial)
833
+ and isinstance(deserializer.args[0], functools.partial)
834
+ and deserializer.args[0].func == _deserialize_array_encoded # pylint: disable=comparison-with-callable
835
+ ):
836
+ # encoded string may be deserialized to sequence
837
+ return deserializer(obj)
838
+ except: # pylint: disable=bare-except
839
+ pass
812
840
  return type(obj)(_deserialize(deserializer, entry, module) for entry in obj)
813
841
 
814
842
 
@@ -40,6 +40,7 @@ class Property(BaseModel): # pylint: disable=too-many-instance-attributes
40
40
  self.is_multipart_file_input: bool = yaml_data.get("isMultipartFileInput", False)
41
41
  self.flatten = self.yaml_data.get("flatten", False) and not getattr(self.type, "flattened_property", False)
42
42
  self.original_tsp_name: Optional[str] = self.yaml_data.get("originalTspName")
43
+ self.encode: Optional[str] = self.yaml_data.get("encode")
43
44
 
44
45
  def pylint_disable(self) -> str:
45
46
  retval: str = ""
@@ -329,6 +329,8 @@ class DpgModelSerializer(_ModelSerializer):
329
329
  args.append("is_multipart_file_input=True")
330
330
  elif hasattr(prop.type, "encode") and prop.type.encode: # type: ignore
331
331
  args.append(f'format="{prop.type.encode}"') # type: ignore
332
+ elif prop.encode:
333
+ args.append(f'format="{prop.encode}"')
332
334
 
333
335
  if prop.xml_metadata:
334
336
  args.append(f"xml={prop.xml_metadata}")
@@ -5,21 +5,28 @@
5
5
  {% set enable_custom_handling = "\n* " in doc_string or doc_string.startswith("* ") %}
6
6
  {%- if enable_custom_handling -%}
7
7
  {%- set lines = doc_string.split('\n') -%}
8
+ {%- set base_indent = wrap_string.lstrip('\n') -%}
8
9
  {%- set result_lines = [] -%}
9
10
  {%- for line in lines -%}
10
11
  {%- if line.startswith('* ') -%}
11
12
  {# Handle bullet points with proper continuation alignment #}
12
13
  {%- set bullet_content = line[2:] -%}
13
- {%- set base_indent = wrap_string.lstrip('\n') -%}
14
14
  {%- set bullet_line = base_indent + ' * ' + bullet_content -%}
15
15
  {%- set continuation_spaces = base_indent + ' ' -%}
16
16
  {%- set wrapped = bullet_line | wordwrap(width=95, break_long_words=False, break_on_hyphens=False, wrapstring='\n' + continuation_spaces) -%}
17
17
  {%- set _ = result_lines.append(wrapped) -%}
18
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) -%}
19
+ {%- set line_indent = '' if line.strip().startswith(':') or loop.index == 1 else (base_indent + ' ') -%}
20
+ {%- set wrapped = (line_indent + line) | wordwrap(width=95, break_long_words=False, break_on_hyphens=False, wrapstring=wrap_string) -%}
21
+ {%- for line in wrapped.split('\n') -%}
22
+ {%- set prefix = "" if loop.index == 1 else " " -%}
23
+ {%- set _ = result_lines.append(prefix + line) -%}
24
+ {%- endfor -%}
21
25
  {%- else -%}
22
- {%- set _ = result_lines.append('') -%}
26
+ {# Do not add continuous blank lines #}
27
+ {%- if (result_lines and result_lines[-1] != '') or not result_lines -%}
28
+ {%- set _ = result_lines.append('') -%}
29
+ {%- endif -%}
23
30
  {%- endif -%}
24
31
  {%- endfor -%}
25
32
  {%- set original_result = result_lines | join('\n') -%}
@@ -37,4 +44,4 @@
37
44
  {% set suffix = suffix_string if list_result | length == loop.index %}
38
45
  {{ prefix }}{{ line }}{{ suffix }}
39
46
  {% endfor %}
40
- {% endmacro %}
47
+ {% endmacro %}
@@ -179,6 +179,19 @@ _VALID_RFC7231 = re.compile(
179
179
  r"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s\d{4}\s\d{2}:\d{2}:\d{2}\sGMT"
180
180
  )
181
181
 
182
+ _ARRAY_ENCODE_MAPPING = {
183
+ "pipeDelimited": "|",
184
+ "spaceDelimited": " ",
185
+ "commaDelimited": ",",
186
+ "newlineDelimited": "\n",
187
+ }
188
+
189
+ def _deserialize_array_encoded(delimit: str, attr):
190
+ if isinstance(attr, str):
191
+ if attr == "":
192
+ return []
193
+ return attr.split(delimit)
194
+ return attr
182
195
 
183
196
  def _deserialize_datetime(attr: typing.Union[str, datetime]) -> datetime:
184
197
  """Deserialize ISO-8601 formatted string into Datetime object.
@@ -323,6 +336,8 @@ _DESERIALIZE_MAPPING_WITHFORMAT = {
323
336
  def get_deserializer(annotation: typing.Any, rf: typing.Optional["_RestField"] = None):
324
337
  if annotation is int and rf and rf._format == "str":
325
338
  return _deserialize_int_as_str
339
+ if annotation is str and rf and rf._format in _ARRAY_ENCODE_MAPPING:
340
+ return functools.partial(_deserialize_array_encoded, _ARRAY_ENCODE_MAPPING[rf._format])
326
341
  if rf and rf._format:
327
342
  return _DESERIALIZE_MAPPING_WITHFORMAT.get(rf._format)
328
343
  {% if code_model.has_external_type %}
@@ -497,6 +512,8 @@ def _is_model(obj: typing.Any) -> bool:
497
512
 
498
513
  def _serialize(o, format: typing.Optional[str] = None): # pylint: disable=too-many-return-statements
499
514
  if isinstance(o, list):
515
+ if format in _ARRAY_ENCODE_MAPPING and all(isinstance(x, str) for x in o):
516
+ return _ARRAY_ENCODE_MAPPING[format].join(o)
500
517
  return [_serialize(x, format) for x in o]
501
518
  if isinstance(o, dict):
502
519
  return {k: _serialize(v, format) for k, v in o.items()}
@@ -809,6 +826,17 @@ def _deserialize_sequence(
809
826
  return obj
810
827
  if isinstance(obj, ET.Element):
811
828
  obj = list(obj)
829
+ try:
830
+ if (
831
+ isinstance(obj, str)
832
+ and isinstance(deserializer, functools.partial)
833
+ and isinstance(deserializer.args[0], functools.partial)
834
+ and deserializer.args[0].func == _deserialize_array_encoded # pylint: disable=comparison-with-callable
835
+ ):
836
+ # encoded string may be deserialized to sequence
837
+ return deserializer(obj)
838
+ except: # pylint: disable=bare-except
839
+ pass
812
840
  return type(obj)(_deserialize(deserializer, entry, module) for entry in obj)
813
841
 
814
842
 
@@ -52,6 +52,7 @@ azure-mgmt-core==1.6.0
52
52
  -e ./generated/authentication-oauth2
53
53
  -e ./generated/authentication-union
54
54
  -e ./generated/setuppy-authentication-union
55
+ -e ./generated/specs-documentation
55
56
  -e ./generated/encode-duration
56
57
  -e ./generated/encode-numeric
57
58
  -e ./generated/encode-array
@@ -0,0 +1,43 @@
1
+ # -------------------------------------------------------------------------
2
+ # Copyright (c) Microsoft Corporation. All rights reserved.
3
+ # Licensed under the MIT License. See License.txt in the project root for
4
+ # license information.
5
+ # --------------------------------------------------------------------------
6
+
7
+ import pytest
8
+ from encode.array.aio import ArrayClient
9
+ from encode.array import models
10
+
11
+
12
+ @pytest.fixture
13
+ async def client():
14
+ async with ArrayClient() as client:
15
+ yield client
16
+
17
+
18
+ @pytest.mark.asyncio
19
+ async def test_comma_delimited(client: ArrayClient):
20
+ body = models.CommaDelimitedArrayProperty(value=["blue", "red", "green"])
21
+ result = await client.property.comma_delimited(body)
22
+ assert result.value == ["blue", "red", "green"]
23
+
24
+
25
+ @pytest.mark.asyncio
26
+ async def test_space_delimited(client: ArrayClient):
27
+ body = models.SpaceDelimitedArrayProperty(value=["blue", "red", "green"])
28
+ result = await client.property.space_delimited(body)
29
+ assert result.value == ["blue", "red", "green"]
30
+
31
+
32
+ @pytest.mark.asyncio
33
+ async def test_pipe_delimited(client: ArrayClient):
34
+ body = models.PipeDelimitedArrayProperty(value=["blue", "red", "green"])
35
+ result = await client.property.pipe_delimited(body)
36
+ assert result.value == ["blue", "red", "green"]
37
+
38
+
39
+ @pytest.mark.asyncio
40
+ async def test_newline_delimited(client: ArrayClient):
41
+ body = models.NewlineDelimitedArrayProperty(value=["blue", "red", "green"])
42
+ result = await client.property.newline_delimited(body)
43
+ assert result.value == ["blue", "red", "green"]
@@ -0,0 +1,60 @@
1
+ # -------------------------------------------------------------------------
2
+ # Copyright (c) Microsoft Corporation. All rights reserved.
3
+ # Licensed under the MIT License. See License.txt in the project root for
4
+ # license information.
5
+ # --------------------------------------------------------------------------
6
+
7
+ import pytest
8
+ from specs.documentation.aio import DocumentationClient
9
+ from specs.documentation import models
10
+
11
+
12
+ @pytest.fixture
13
+ async def client():
14
+ async with DocumentationClient(endpoint="http://localhost:3000") as client:
15
+ yield client
16
+
17
+
18
+ class TestLists:
19
+ @pytest.mark.asyncio
20
+ async def test_bullet_points_op(self, client: DocumentationClient):
21
+ # GET /documentation/lists/bullet-points/op
22
+ # Expected: 204 No Content
23
+ await client.lists.bullet_points_op()
24
+
25
+ @pytest.mark.skip(reason="https://github.com/microsoft/typespec/issues/9173")
26
+ @pytest.mark.asyncio
27
+ async def test_bullet_points_model(self, client: DocumentationClient):
28
+ # POST /documentation/lists/bullet-points/model
29
+ # Expected request body: {"prop": "Simple"}
30
+ # Expected: 200 OK
31
+ await client.lists.bullet_points_model(input=models.BulletPointsModel(prop="Simple"))
32
+
33
+ # Also test with JSON
34
+ await client.lists.bullet_points_model(body={"input": {"prop": "Simple"}})
35
+
36
+ @pytest.mark.asyncio
37
+ async def test_numbered(self, client: DocumentationClient):
38
+ # GET /documentation/lists/numbered
39
+ # Expected: 204 No Content
40
+ await client.lists.numbered()
41
+
42
+
43
+ class TestTextFormatting:
44
+ @pytest.mark.asyncio
45
+ async def test_bold_text(self, client: DocumentationClient):
46
+ # GET /documentation/text-formatting/bold
47
+ # Expected: 204 No Content
48
+ await client.text_formatting.bold_text()
49
+
50
+ @pytest.mark.asyncio
51
+ async def test_italic_text(self, client: DocumentationClient):
52
+ # GET /documentation/text-formatting/italic
53
+ # Expected: 204 No Content
54
+ await client.text_formatting.italic_text()
55
+
56
+ @pytest.mark.asyncio
57
+ async def test_combined_formatting(self, client: DocumentationClient):
58
+ # GET /documentation/text-formatting/combined
59
+ # Expected: 204 No Content
60
+ await client.text_formatting.combined_formatting()
@@ -0,0 +1,38 @@
1
+ # -------------------------------------------------------------------------
2
+ # Copyright (c) Microsoft Corporation. All rights reserved.
3
+ # Licensed under the MIT License. See License.txt in the project root for
4
+ # license information.
5
+ # --------------------------------------------------------------------------
6
+
7
+ import pytest
8
+ from encode.array import ArrayClient, models
9
+
10
+
11
+ @pytest.fixture
12
+ def client():
13
+ with ArrayClient() as client:
14
+ yield client
15
+
16
+
17
+ def test_comma_delimited(client: ArrayClient):
18
+ body = models.CommaDelimitedArrayProperty(value=["blue", "red", "green"])
19
+ result = client.property.comma_delimited(body)
20
+ assert result.value == ["blue", "red", "green"]
21
+
22
+
23
+ def test_space_delimited(client: ArrayClient):
24
+ body = models.SpaceDelimitedArrayProperty(value=["blue", "red", "green"])
25
+ result = client.property.space_delimited(body)
26
+ assert result.value == ["blue", "red", "green"]
27
+
28
+
29
+ def test_pipe_delimited(client: ArrayClient):
30
+ body = models.PipeDelimitedArrayProperty(value=["blue", "red", "green"])
31
+ result = client.property.pipe_delimited(body)
32
+ assert result.value == ["blue", "red", "green"]
33
+
34
+
35
+ def test_newline_delimited(client: ArrayClient):
36
+ body = models.NewlineDelimitedArrayProperty(value=["blue", "red", "green"])
37
+ result = client.property.newline_delimited(body)
38
+ assert result.value == ["blue", "red", "green"]
@@ -0,0 +1,52 @@
1
+ # -------------------------------------------------------------------------
2
+ # Copyright (c) Microsoft Corporation. All rights reserved.
3
+ # Licensed under the MIT License. See License.txt in the project root for
4
+ # license information.
5
+ # --------------------------------------------------------------------------
6
+
7
+ import pytest
8
+ from specs.documentation import DocumentationClient, models
9
+
10
+
11
+ @pytest.fixture
12
+ def client():
13
+ with DocumentationClient(endpoint="http://localhost:3000") as client:
14
+ yield client
15
+
16
+
17
+ class TestLists:
18
+ def test_bullet_points_op(self, client: DocumentationClient):
19
+ # GET /documentation/lists/bullet-points/op
20
+ # Expected: 204 No Content
21
+ client.lists.bullet_points_op()
22
+
23
+ @pytest.mark.skip(reason="https://github.com/microsoft/typespec/issues/9173")
24
+ def test_bullet_points_model(self, client: DocumentationClient):
25
+ # POST /documentation/lists/bullet-points/model
26
+ # Expected: 200 OK
27
+ client.lists.bullet_points_model(input=models.BulletPointsModel(prop="Simple"))
28
+
29
+ # Also test with JSON
30
+ client.lists.bullet_points_model(body={"input": {"prop": "Simple"}})
31
+
32
+ def test_numbered(self, client: DocumentationClient):
33
+ # GET /documentation/lists/numbered
34
+ # Expected: 204 No Content
35
+ client.lists.numbered()
36
+
37
+
38
+ class TestTextFormatting:
39
+ def test_bold_text(self, client: DocumentationClient):
40
+ # GET /documentation/text-formatting/bold
41
+ # Expected: 204 No Content
42
+ client.text_formatting.bold_text()
43
+
44
+ def test_italic_text(self, client: DocumentationClient):
45
+ # GET /documentation/text-formatting/italic
46
+ # Expected: 204 No Content
47
+ client.text_formatting.italic_text()
48
+
49
+ def test_combined_formatting(self, client: DocumentationClient):
50
+ # GET /documentation/text-formatting/combined
51
+ # Expected: 204 No Content
52
+ client.text_formatting.combined_formatting()
@@ -7,6 +7,7 @@
7
7
  -e ./generated/authentication-oauth2
8
8
  -e ./generated/authentication-union
9
9
  -e ./generated/setuppy-authentication-union
10
+ -e ./generated/specs-documentation
10
11
  -e ./generated/encode-duration
11
12
  -e ./generated/encode-numeric
12
13
  -e ./generated/encode-array