@typespec/http-client-python 0.4.4 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/emitter/emitter.d.ts.map +1 -1
- package/dist/emitter/emitter.js +110 -24
- package/dist/emitter/emitter.js.map +1 -1
- package/dist/emitter/http.js +1 -1
- package/dist/emitter/http.js.map +1 -1
- package/dist/emitter/lib.d.ts +1 -0
- package/dist/emitter/lib.d.ts.map +1 -1
- package/dist/emitter/lib.js +1 -0
- package/dist/emitter/lib.js.map +1 -1
- package/dist/emitter/run-python3.d.ts +2 -0
- package/dist/emitter/run-python3.d.ts.map +1 -0
- package/dist/emitter/run-python3.js +19 -0
- package/dist/emitter/run-python3.js.map +1 -0
- package/dist/emitter/system-requirements.d.ts +17 -0
- package/dist/emitter/system-requirements.d.ts.map +1 -0
- package/dist/emitter/system-requirements.js +167 -0
- package/dist/emitter/system-requirements.js.map +1 -0
- package/emitter/src/emitter.ts +111 -23
- package/emitter/src/http.ts +1 -1
- package/emitter/src/lib.ts +2 -0
- package/emitter/src/run-python3.ts +20 -0
- package/emitter/src/system-requirements.ts +261 -0
- package/emitter/temp/tsconfig.tsbuildinfo +1 -1
- package/eng/scripts/Test-Packages.ps1 +1 -1
- package/eng/scripts/ci/regenerate.ts +20 -8
- package/eng/scripts/setup/__pycache__/venvtools.cpython-38.pyc +0 -0
- package/eng/scripts/setup/build.ts +16 -0
- package/eng/scripts/setup/build_pygen_wheel.py +40 -0
- package/eng/scripts/setup/install.py +9 -8
- package/eng/scripts/setup/install.ts +12 -0
- package/eng/scripts/setup/prepare.py +3 -1
- package/eng/scripts/setup/prepare.ts +11 -0
- package/eng/scripts/setup/run-python3.ts +1 -6
- package/generator/build/lib/pygen/__init__.py +107 -0
- package/generator/build/lib/pygen/_version.py +7 -0
- package/generator/build/lib/pygen/black.py +71 -0
- package/generator/build/lib/pygen/codegen/__init__.py +357 -0
- package/generator/build/lib/pygen/codegen/_utils.py +17 -0
- package/generator/build/lib/pygen/codegen/models/__init__.py +204 -0
- package/generator/build/lib/pygen/codegen/models/base.py +186 -0
- package/generator/build/lib/pygen/codegen/models/base_builder.py +118 -0
- package/generator/build/lib/pygen/codegen/models/client.py +435 -0
- package/generator/build/lib/pygen/codegen/models/code_model.py +237 -0
- package/generator/build/lib/pygen/codegen/models/combined_type.py +149 -0
- package/generator/build/lib/pygen/codegen/models/constant_type.py +129 -0
- package/generator/build/lib/pygen/codegen/models/credential_types.py +214 -0
- package/generator/build/lib/pygen/codegen/models/dictionary_type.py +127 -0
- package/generator/build/lib/pygen/codegen/models/enum_type.py +238 -0
- package/generator/build/lib/pygen/codegen/models/imports.py +291 -0
- package/generator/build/lib/pygen/codegen/models/list_type.py +143 -0
- package/generator/build/lib/pygen/codegen/models/lro_operation.py +142 -0
- package/generator/build/lib/pygen/codegen/models/lro_paging_operation.py +32 -0
- package/generator/build/lib/pygen/codegen/models/model_type.py +357 -0
- package/generator/build/lib/pygen/codegen/models/operation.py +509 -0
- package/generator/build/lib/pygen/codegen/models/operation_group.py +184 -0
- package/generator/build/lib/pygen/codegen/models/paging_operation.py +155 -0
- package/generator/build/lib/pygen/codegen/models/parameter.py +412 -0
- package/generator/build/lib/pygen/codegen/models/parameter_list.py +387 -0
- package/generator/build/lib/pygen/codegen/models/primitive_types.py +659 -0
- package/generator/build/lib/pygen/codegen/models/property.py +170 -0
- package/generator/build/lib/pygen/codegen/models/request_builder.py +189 -0
- package/generator/build/lib/pygen/codegen/models/request_builder_parameter.py +115 -0
- package/generator/build/lib/pygen/codegen/models/response.py +348 -0
- package/generator/build/lib/pygen/codegen/models/utils.py +21 -0
- package/generator/build/lib/pygen/codegen/serializers/__init__.py +574 -0
- package/generator/build/lib/pygen/codegen/serializers/base_serializer.py +21 -0
- package/generator/build/lib/pygen/codegen/serializers/builder_serializer.py +1533 -0
- package/generator/build/lib/pygen/codegen/serializers/client_serializer.py +294 -0
- package/generator/build/lib/pygen/codegen/serializers/enum_serializer.py +15 -0
- package/generator/build/lib/pygen/codegen/serializers/general_serializer.py +213 -0
- package/generator/build/lib/pygen/codegen/serializers/import_serializer.py +126 -0
- package/generator/build/lib/pygen/codegen/serializers/metadata_serializer.py +198 -0
- package/generator/build/lib/pygen/codegen/serializers/model_init_serializer.py +33 -0
- package/generator/build/lib/pygen/codegen/serializers/model_serializer.py +335 -0
- package/generator/build/lib/pygen/codegen/serializers/operation_groups_serializer.py +89 -0
- package/generator/build/lib/pygen/codegen/serializers/operations_init_serializer.py +44 -0
- package/generator/build/lib/pygen/codegen/serializers/parameter_serializer.py +221 -0
- package/generator/build/lib/pygen/codegen/serializers/patch_serializer.py +19 -0
- package/generator/build/lib/pygen/codegen/serializers/request_builders_serializer.py +52 -0
- package/generator/build/lib/pygen/codegen/serializers/sample_serializer.py +168 -0
- package/generator/build/lib/pygen/codegen/serializers/test_serializer.py +292 -0
- package/generator/build/lib/pygen/codegen/serializers/types_serializer.py +31 -0
- package/generator/build/lib/pygen/codegen/serializers/utils.py +68 -0
- package/generator/build/lib/pygen/codegen/templates/client.py.jinja2 +37 -0
- package/generator/build/lib/pygen/codegen/templates/client_container.py.jinja2 +12 -0
- package/generator/build/lib/pygen/codegen/templates/config.py.jinja2 +73 -0
- package/generator/build/lib/pygen/codegen/templates/config_container.py.jinja2 +16 -0
- package/generator/build/lib/pygen/codegen/templates/conftest.py.jinja2 +28 -0
- package/generator/build/lib/pygen/codegen/templates/enum.py.jinja2 +13 -0
- package/generator/build/lib/pygen/codegen/templates/enum_container.py.jinja2 +10 -0
- package/generator/build/lib/pygen/codegen/templates/init.py.jinja2 +24 -0
- package/generator/build/lib/pygen/codegen/templates/keywords.jinja2 +27 -0
- package/generator/build/lib/pygen/codegen/templates/lro_operation.py.jinja2 +16 -0
- package/generator/build/lib/pygen/codegen/templates/lro_paging_operation.py.jinja2 +18 -0
- package/generator/build/lib/pygen/codegen/templates/macros.jinja2 +12 -0
- package/generator/build/lib/pygen/codegen/templates/metadata.json.jinja2 +167 -0
- package/generator/build/lib/pygen/codegen/templates/model_base.py.jinja2 +1174 -0
- package/generator/build/lib/pygen/codegen/templates/model_container.py.jinja2 +15 -0
- package/generator/build/lib/pygen/codegen/templates/model_dpg.py.jinja2 +97 -0
- package/generator/build/lib/pygen/codegen/templates/model_init.py.jinja2 +33 -0
- package/generator/build/lib/pygen/codegen/templates/model_msrest.py.jinja2 +92 -0
- package/generator/build/lib/pygen/codegen/templates/operation.py.jinja2 +21 -0
- package/generator/build/lib/pygen/codegen/templates/operation_group.py.jinja2 +75 -0
- package/generator/build/lib/pygen/codegen/templates/operation_groups_container.py.jinja2 +19 -0
- package/generator/build/lib/pygen/codegen/templates/operation_tools.jinja2 +81 -0
- package/generator/build/lib/pygen/codegen/templates/operations_folder_init.py.jinja2 +17 -0
- package/generator/build/lib/pygen/codegen/templates/packaging_templates/CHANGELOG.md.jinja2 +6 -0
- package/generator/build/lib/pygen/codegen/templates/packaging_templates/LICENSE.jinja2 +21 -0
- package/generator/build/lib/pygen/codegen/templates/packaging_templates/MANIFEST.in.jinja2 +8 -0
- package/generator/build/lib/pygen/codegen/templates/packaging_templates/README.md.jinja2 +107 -0
- package/generator/build/lib/pygen/codegen/templates/packaging_templates/dev_requirements.txt.jinja2 +9 -0
- package/generator/build/lib/pygen/codegen/templates/packaging_templates/setup.py.jinja2 +108 -0
- package/generator/build/lib/pygen/codegen/templates/paging_operation.py.jinja2 +21 -0
- package/generator/build/lib/pygen/codegen/templates/patch.py.jinja2 +19 -0
- package/generator/build/lib/pygen/codegen/templates/pkgutil_init.py.jinja2 +1 -0
- package/generator/build/lib/pygen/codegen/templates/request_builder.py.jinja2 +28 -0
- package/generator/build/lib/pygen/codegen/templates/request_builders.py.jinja2 +10 -0
- package/generator/build/lib/pygen/codegen/templates/rest_init.py.jinja2 +12 -0
- package/generator/build/lib/pygen/codegen/templates/sample.py.jinja2 +44 -0
- package/generator/build/lib/pygen/codegen/templates/serialization.py.jinja2 +2117 -0
- package/generator/build/lib/pygen/codegen/templates/test.py.jinja2 +50 -0
- package/generator/build/lib/pygen/codegen/templates/testpreparer.py.jinja2 +26 -0
- package/generator/build/lib/pygen/codegen/templates/types.py.jinja2 +7 -0
- package/generator/build/lib/pygen/codegen/templates/validation.py.jinja2 +38 -0
- package/generator/build/lib/pygen/codegen/templates/vendor.py.jinja2 +96 -0
- package/generator/build/lib/pygen/codegen/templates/version.py.jinja2 +4 -0
- package/generator/build/lib/pygen/m2r.py +65 -0
- package/generator/build/lib/pygen/preprocess/__init__.py +515 -0
- package/generator/build/lib/pygen/preprocess/helpers.py +27 -0
- package/generator/build/lib/pygen/preprocess/python_mappings.py +226 -0
- package/generator/build/lib/pygen/utils.py +163 -0
- package/generator/component-detection-pip-report.json +134 -0
- package/generator/dev_requirements.txt +0 -1
- package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
- package/generator/pygen/codegen/__init__.py +4 -4
- package/generator/pygen.egg-info/PKG-INFO +7 -4
- package/generator/pygen.egg-info/requires.txt +7 -4
- package/generator/setup.py +7 -4
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_flatten_async.py +1 -1
- package/generator/test/{generic_mock_api_tests/asynctests/test_payload_pageable_async.py → azure/mock_api_tests/asynctests/test_azure_payload_pageable_async.py} +1 -1
- package/generator/test/azure/mock_api_tests/conftest.py +5 -4
- package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_flatten.py +1 -1
- package/generator/test/{generic_mock_api_tests/test_payload_pageable.py → azure/mock_api_tests/test_azure_payload_pageable.py} +1 -1
- package/generator/test/{generic_mock_api_tests → azure/mock_api_tests}/test_resiliency_srv_driven.py +4 -2
- package/generator/test/{generic_mock_api_tests/asynctests → azure/mock_api_tests}/test_resiliency_srv_driven_async.py +3 -2
- package/generator/test/azure/requirements.txt +9 -8
- package/generator/test/generic_mock_api_tests/conftest.py +9 -4
- package/generator/test/unbranded/mock_api_tests/conftest.py +4 -4
- package/generator/test/unbranded/mock_api_tests/test_unbranded.py +1 -1
- package/generator/test/unbranded/requirements.txt +1 -8
- package/package.json +10 -10
- package/generator/requirements.txt +0 -12
- /package/generator/test/{generic_mock_api_tests → azure/mock_api_tests}/asynctests/test_client_naming_async.py +0 -0
- /package/generator/test/{generic_mock_api_tests → azure/mock_api_tests}/asynctests/test_client_structure_async.py +0 -0
- /package/generator/test/{generic_mock_api_tests → azure/mock_api_tests}/test_client_naming.py +0 -0
- /package/generator/test/{generic_mock_api_tests → azure/mock_api_tests}/test_client_structure.py +0 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
# -------------------------------------------------------------------------
|
|
4
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
5
|
+
# Licensed under the MIT License. See License.txt in the project root for
|
|
6
|
+
# license information.
|
|
7
|
+
# --------------------------------------------------------------------------
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
if not sys.version_info >= (3, 8, 0):
|
|
11
|
+
raise Exception("Autorest for Python extension requires Python 3.8 at least")
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
import pip
|
|
15
|
+
except ImportError:
|
|
16
|
+
raise Exception("Your Python installation doesn't have pip available")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Now we have pip and Py >= 3.8, go to work
|
|
20
|
+
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
from venvtools import ExtendedEnvBuilder, python_run
|
|
24
|
+
|
|
25
|
+
_ROOT_DIR = Path(__file__).parent.parent.parent.parent
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def main():
|
|
29
|
+
venv_path = _ROOT_DIR / "venv_build_wheel"
|
|
30
|
+
env_builder = ExtendedEnvBuilder(with_pip=True, upgrade_deps=True)
|
|
31
|
+
env_builder.create(venv_path)
|
|
32
|
+
venv_context = env_builder.context
|
|
33
|
+
|
|
34
|
+
python_run(venv_context, "pip", ["install", "-U", "pip"])
|
|
35
|
+
python_run(venv_context, "pip", ["install", "build"])
|
|
36
|
+
python_run(venv_context, "build", ["--wheel"], additional_dir="generator")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
if __name__ == "__main__":
|
|
40
|
+
main()
|
|
@@ -8,17 +8,23 @@
|
|
|
8
8
|
import sys
|
|
9
9
|
|
|
10
10
|
if not sys.version_info >= (3, 8, 0):
|
|
11
|
-
raise
|
|
11
|
+
raise Warning(
|
|
12
|
+
"Autorest for Python extension requires Python 3.8 at least. We will run your code with Pyodide since your Python version isn't adequate."
|
|
13
|
+
)
|
|
12
14
|
|
|
13
15
|
try:
|
|
14
16
|
import pip
|
|
15
17
|
except ImportError:
|
|
16
|
-
raise
|
|
18
|
+
raise Warning(
|
|
19
|
+
"Your Python installation doesn't have pip available. We will run your code with Pyodide since your Python version isn't adequate."
|
|
20
|
+
)
|
|
17
21
|
|
|
18
22
|
try:
|
|
19
23
|
import venv
|
|
20
24
|
except ImportError:
|
|
21
|
-
raise
|
|
25
|
+
raise Warning(
|
|
26
|
+
"Your Python installation doesn't have venv available. We will run your code with Pyodide since your Python version isn't adequate."
|
|
27
|
+
)
|
|
22
28
|
|
|
23
29
|
|
|
24
30
|
# Now we have pip and Py >= 3.8, go to work
|
|
@@ -41,11 +47,6 @@ def main():
|
|
|
41
47
|
venv_context = env_builder.context
|
|
42
48
|
|
|
43
49
|
python_run(venv_context, "pip", ["install", "-U", "pip"])
|
|
44
|
-
python_run(
|
|
45
|
-
venv_context,
|
|
46
|
-
"pip",
|
|
47
|
-
["install", "-r", f"{_ROOT_DIR}/generator/requirements.txt"],
|
|
48
|
-
)
|
|
49
50
|
python_run(venv_context, "pip", ["install", "-e", f"{_ROOT_DIR}/generator"])
|
|
50
51
|
|
|
51
52
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { runPython3 } from "./run-python3.js";
|
|
2
|
+
|
|
3
|
+
async function main() {
|
|
4
|
+
try {
|
|
5
|
+
await runPython3("./eng/scripts/setup/install.py");
|
|
6
|
+
console.log("Found Python on your local environment and created a venv with all requirements."); // eslint-disable-line no-console
|
|
7
|
+
} catch (error) {
|
|
8
|
+
console.log("No Python found on your local environment. We will use Pyodide instead."); // eslint-disable-line no-console
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
main();
|
|
@@ -10,7 +10,9 @@ import os
|
|
|
10
10
|
import argparse
|
|
11
11
|
|
|
12
12
|
if not sys.version_info >= (3, 8, 0):
|
|
13
|
-
raise
|
|
13
|
+
raise Warning(
|
|
14
|
+
"Autorest for Python extension requires Python 3.8 at least. We will run your code with Pyodide since your Python version isn't adequate."
|
|
15
|
+
)
|
|
14
16
|
|
|
15
17
|
from pathlib import Path
|
|
16
18
|
import venv
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { runPython3 } from "./run-python3.js";
|
|
2
|
+
|
|
3
|
+
async function main() {
|
|
4
|
+
try {
|
|
5
|
+
await runPython3("./eng/scripts/setup/prepare.py");
|
|
6
|
+
} catch (error) {
|
|
7
|
+
console.log("No Python found on your local environment. We will use Pyodide instead."); // eslint-disable-line no-console
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
main();
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import cp from "child_process";
|
|
10
10
|
import { patchPythonPath } from "./system-requirements.js";
|
|
11
11
|
|
|
12
|
-
async function runPython3(...args: string[]) {
|
|
12
|
+
export async function runPython3(...args: string[]) {
|
|
13
13
|
const command = await patchPythonPath(["python", ...args], {
|
|
14
14
|
version: ">=3.8",
|
|
15
15
|
environmentVariable: "AUTOREST_PYTHON_EXE",
|
|
@@ -18,8 +18,3 @@ async function runPython3(...args: string[]) {
|
|
|
18
18
|
stdio: [0, 1, 2],
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
|
-
|
|
22
|
-
runPython3(...process.argv.slice(2)).catch((err) => {
|
|
23
|
-
console.error(err.toString()); // eslint-disable-line no-console
|
|
24
|
-
process.exit(1);
|
|
25
|
-
});
|
|
@@ -0,0 +1,107 @@
|
|
|
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
|
+
import logging
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
import json
|
|
9
|
+
from abc import ABC, abstractmethod
|
|
10
|
+
from typing import Any, Dict, Union, List
|
|
11
|
+
|
|
12
|
+
import yaml
|
|
13
|
+
|
|
14
|
+
from ._version import VERSION
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
__version__ = VERSION
|
|
18
|
+
_LOGGER = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ReaderAndWriter:
|
|
22
|
+
def __init__(self, *, output_folder: Union[str, Path], **kwargs: Any) -> None:
|
|
23
|
+
self.output_folder = Path(output_folder)
|
|
24
|
+
self._list_file: List[str] = []
|
|
25
|
+
try:
|
|
26
|
+
with open(
|
|
27
|
+
Path(self.output_folder) / Path("..") / Path("python.json"),
|
|
28
|
+
"r",
|
|
29
|
+
encoding="utf-8-sig",
|
|
30
|
+
) as fd:
|
|
31
|
+
python_json = json.load(fd)
|
|
32
|
+
except Exception: # pylint: disable=broad-except
|
|
33
|
+
python_json = {}
|
|
34
|
+
self.options = kwargs
|
|
35
|
+
if python_json:
|
|
36
|
+
_LOGGER.warning("Loading python.json file. This behavior will be depreacted")
|
|
37
|
+
self.options.update(python_json)
|
|
38
|
+
|
|
39
|
+
def read_file(self, path: Union[str, Path]) -> str:
|
|
40
|
+
"""Directly reading from disk"""
|
|
41
|
+
# make path relative to output folder
|
|
42
|
+
try:
|
|
43
|
+
with open(self.output_folder / Path(path), "r", encoding="utf-8-sig") as fd:
|
|
44
|
+
return fd.read()
|
|
45
|
+
except FileNotFoundError:
|
|
46
|
+
return ""
|
|
47
|
+
|
|
48
|
+
def write_file(self, filename: Union[str, Path], file_content: str) -> None:
|
|
49
|
+
"""Directly writing to disk"""
|
|
50
|
+
file_folder = Path(filename).parent
|
|
51
|
+
if not Path.is_dir(self.output_folder / file_folder):
|
|
52
|
+
Path.mkdir(self.output_folder / file_folder, parents=True)
|
|
53
|
+
with open(self.output_folder / Path(filename), "w", encoding="utf-8") as fd:
|
|
54
|
+
fd.write(file_content)
|
|
55
|
+
|
|
56
|
+
def list_file(self) -> List[str]:
|
|
57
|
+
return [str(f.relative_to(self.output_folder)) for f in self.output_folder.glob("**/*") if f.is_file()]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class Plugin(ReaderAndWriter, ABC):
|
|
61
|
+
"""A base class for autorest plugin.
|
|
62
|
+
|
|
63
|
+
:param autorestapi: An autorest API instance
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
@abstractmethod
|
|
67
|
+
def process(self) -> bool:
|
|
68
|
+
"""The plugin process.
|
|
69
|
+
|
|
70
|
+
:rtype: bool
|
|
71
|
+
:returns: True if everything's ok, False optherwise
|
|
72
|
+
:raises Exception: Could raise any exception, stacktrace will be sent to autorest API
|
|
73
|
+
"""
|
|
74
|
+
raise NotImplementedError()
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class YamlUpdatePlugin(Plugin):
|
|
78
|
+
"""A plugin that update the YAML as input."""
|
|
79
|
+
|
|
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:
|
|
83
|
+
return yaml.safe_load(fd.read())
|
|
84
|
+
|
|
85
|
+
def write_yaml(self, yaml_string: str) -> None:
|
|
86
|
+
with open(self.options["cadl_file"], "w", encoding="utf-8-sig") as fd:
|
|
87
|
+
fd.write(yaml_string)
|
|
88
|
+
|
|
89
|
+
def process(self) -> bool:
|
|
90
|
+
# List the input file, should be only one
|
|
91
|
+
yaml_data = self.get_yaml()
|
|
92
|
+
|
|
93
|
+
self.update_yaml(yaml_data)
|
|
94
|
+
|
|
95
|
+
yaml_string = yaml.safe_dump(yaml_data)
|
|
96
|
+
|
|
97
|
+
self.write_yaml(yaml_string)
|
|
98
|
+
return True
|
|
99
|
+
|
|
100
|
+
@abstractmethod
|
|
101
|
+
def update_yaml(self, yaml_data: Dict[str, Any]) -> None:
|
|
102
|
+
"""The code-model-v4-no-tags yaml model tree.
|
|
103
|
+
|
|
104
|
+
:rtype: updated yaml
|
|
105
|
+
:raises Exception: Could raise any exception, stacktrace will be sent to autorest API
|
|
106
|
+
"""
|
|
107
|
+
raise NotImplementedError()
|
|
@@ -0,0 +1,7 @@
|
|
|
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
|
+
VERSION = "0.1.0"
|
|
@@ -0,0 +1,71 @@
|
|
|
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
|
+
import logging
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
import os
|
|
9
|
+
import black
|
|
10
|
+
from black.report import NothingChanged
|
|
11
|
+
|
|
12
|
+
from . import Plugin
|
|
13
|
+
from .utils import parse_args
|
|
14
|
+
|
|
15
|
+
_LOGGER = logging.getLogger("blib2to3")
|
|
16
|
+
|
|
17
|
+
_BLACK_MODE = black.Mode() # pyright: ignore [reportPrivateImportUsage]
|
|
18
|
+
_BLACK_MODE.line_length = 120
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BlackScriptPlugin(Plugin):
|
|
22
|
+
def __init__(self, **kwargs):
|
|
23
|
+
super().__init__(**kwargs)
|
|
24
|
+
output_folder = self.options.get("output_folder", str(self.output_folder))
|
|
25
|
+
if output_folder.startswith("file:"):
|
|
26
|
+
output_folder = output_folder[5:]
|
|
27
|
+
if os.name == "nt" and output_folder.startswith("///"):
|
|
28
|
+
output_folder = output_folder[3:]
|
|
29
|
+
self.output_folder = Path(output_folder)
|
|
30
|
+
|
|
31
|
+
def process(self) -> bool:
|
|
32
|
+
# apply format_file on every .py file in the output folder
|
|
33
|
+
list(
|
|
34
|
+
map(
|
|
35
|
+
self.format_file,
|
|
36
|
+
[
|
|
37
|
+
Path(f)
|
|
38
|
+
for f in self.list_file()
|
|
39
|
+
if Path(f).parts[0]
|
|
40
|
+
not in (
|
|
41
|
+
"__pycache__",
|
|
42
|
+
"node_modules",
|
|
43
|
+
"venv",
|
|
44
|
+
"env",
|
|
45
|
+
)
|
|
46
|
+
and not Path(f).parts[0].startswith(".")
|
|
47
|
+
and Path(f).suffix == ".py"
|
|
48
|
+
],
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
return True
|
|
52
|
+
|
|
53
|
+
def format_file(self, file: Path) -> None:
|
|
54
|
+
file_content = ""
|
|
55
|
+
try:
|
|
56
|
+
file_content = self.read_file(file)
|
|
57
|
+
file_content = black.format_file_contents(file_content, fast=True, mode=_BLACK_MODE)
|
|
58
|
+
except NothingChanged:
|
|
59
|
+
pass
|
|
60
|
+
except:
|
|
61
|
+
_LOGGER.error("Error: failed to format %s", file)
|
|
62
|
+
raise
|
|
63
|
+
if len(file_content.splitlines()) > 1000:
|
|
64
|
+
file_content = "# pylint: disable=too-many-lines\n" + file_content
|
|
65
|
+
self.write_file(file, file_content)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
if __name__ == "__main__":
|
|
69
|
+
# CADL pipeline will call this
|
|
70
|
+
args, unknown_args = parse_args(need_cadl_file=False)
|
|
71
|
+
BlackScriptPlugin(output_folder=args.output_folder, **unknown_args).process()
|
|
@@ -0,0 +1,357 @@
|
|
|
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
|
+
import logging
|
|
7
|
+
from typing import Dict, Any, Union, Optional
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
import yaml
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
from .. import Plugin
|
|
13
|
+
from ..utils import parse_args
|
|
14
|
+
from .models.code_model import CodeModel
|
|
15
|
+
from .serializers import JinjaSerializer
|
|
16
|
+
from ._utils import DEFAULT_HEADER_TEXT, VALID_PACKAGE_MODE, TYPESPEC_PACKAGE_MODE
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _default_pprint(package_name: str) -> str:
|
|
20
|
+
return " ".join([i.capitalize() for i in package_name.split("-")])
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
_LOGGER = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class OptionsRetriever:
|
|
27
|
+
OPTIONS_TO_DEFAULT = {
|
|
28
|
+
"azure-arm": False,
|
|
29
|
+
"flavor": "azure", # need to default to azure in shared code so we don't break swagger generation
|
|
30
|
+
"no-async": False,
|
|
31
|
+
"low-level-client": False,
|
|
32
|
+
"version-tolerant": True,
|
|
33
|
+
"keep-version-file": False,
|
|
34
|
+
"no-namespace-folders": False,
|
|
35
|
+
"basic-setup-py": False,
|
|
36
|
+
"client-side-validation": False,
|
|
37
|
+
"multiapi": False,
|
|
38
|
+
"polymorphic-examples": 5,
|
|
39
|
+
"generate-sample": False,
|
|
40
|
+
"generate-test": False,
|
|
41
|
+
"from-typespec": False,
|
|
42
|
+
"emit-cross-language-definition-file": False,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def is_azure_flavor(self) -> bool:
|
|
47
|
+
return self.flavor == "azure"
|
|
48
|
+
|
|
49
|
+
def __init__(self, options: Dict[str, Any]) -> None:
|
|
50
|
+
self.options = options
|
|
51
|
+
|
|
52
|
+
def __getattr__(self, prop: str) -> Any:
|
|
53
|
+
key = prop.replace("_", "-")
|
|
54
|
+
return self.options.get(key, self.OPTIONS_TO_DEFAULT.get(key))
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def company_name(self) -> str:
|
|
58
|
+
return self.options.get("company-name", "Microsoft" if self.is_azure_flavor else "")
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def license_header(self) -> str:
|
|
62
|
+
license_header = self.options.get(
|
|
63
|
+
"header-text",
|
|
64
|
+
(DEFAULT_HEADER_TEXT.format(company_name=self.company_name) if self.company_name else ""),
|
|
65
|
+
)
|
|
66
|
+
if license_header:
|
|
67
|
+
license_header = license_header.replace("\n", "\n# ")
|
|
68
|
+
license_header = (
|
|
69
|
+
"# --------------------------------------------------------------------------\n# " + license_header
|
|
70
|
+
)
|
|
71
|
+
license_header += "\n# --------------------------------------------------------------------------"
|
|
72
|
+
return license_header
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def show_operations(self) -> bool:
|
|
76
|
+
return self.options.get("show-operations", not self.low_level_client)
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def _models_mode_default(self) -> str:
|
|
80
|
+
models_mode_default = "none" if self.low_level_client or self.version_tolerant else "msrest"
|
|
81
|
+
if self.options.get("cadl_file") is not None:
|
|
82
|
+
models_mode_default = "dpg"
|
|
83
|
+
return models_mode_default
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def original_models_mode(self) -> str:
|
|
87
|
+
return self.options.get("models-mode", self._models_mode_default)
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def models_mode(self) -> Union[str, bool]:
|
|
91
|
+
# switch to falsy value for easier code writing
|
|
92
|
+
return False if self.original_models_mode == "none" else self.original_models_mode
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def tracing(self) -> bool:
|
|
96
|
+
return self.options.get(
|
|
97
|
+
"tracing",
|
|
98
|
+
self.show_operations and self.is_azure_flavor,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def show_send_request(self) -> bool:
|
|
103
|
+
return self.options.get(
|
|
104
|
+
"show-send-request",
|
|
105
|
+
self._low_level_or_version_tolerant,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def _low_level_or_version_tolerant(self) -> bool:
|
|
110
|
+
return self.low_level_client or self.version_tolerant
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def only_path_and_body_params_positional(self) -> bool:
|
|
114
|
+
return self.options.get(
|
|
115
|
+
"only-path-and-body-params-positional",
|
|
116
|
+
self._low_level_or_version_tolerant,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def combine_operation_files(self) -> bool:
|
|
121
|
+
return self.options.get(
|
|
122
|
+
"combine-operation-files",
|
|
123
|
+
self.version_tolerant,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def package_pprint_name(self) -> str:
|
|
128
|
+
return self.options.get("package-pprint-name") or _default_pprint(str(self.package_name))
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def default_optional_constants_to_none(self) -> bool:
|
|
132
|
+
return self.options.get(
|
|
133
|
+
"default-optional-constants-to-none",
|
|
134
|
+
self._low_level_or_version_tolerant,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
@property
|
|
138
|
+
def builders_visibility(self) -> str:
|
|
139
|
+
builders_visibility = self.options.get("builders-visibility")
|
|
140
|
+
if builders_visibility is None:
|
|
141
|
+
return "public" if self.low_level_client else "embedded"
|
|
142
|
+
return builders_visibility.lower()
|
|
143
|
+
|
|
144
|
+
@property
|
|
145
|
+
def head_as_boolean(self) -> bool:
|
|
146
|
+
head_as_boolean = self.options.get("head-as-boolean", True)
|
|
147
|
+
# Force some options in ARM MODE
|
|
148
|
+
return True if self.azure_arm else head_as_boolean
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
def package_mode(self) -> str:
|
|
152
|
+
return self.options.get("packaging-files-dir") or self.options.get("package-mode", "")
|
|
153
|
+
|
|
154
|
+
@property
|
|
155
|
+
def packaging_files_config(self) -> Optional[Dict[str, Any]]:
|
|
156
|
+
packaging_files_config = self.options.get("packaging-files-config")
|
|
157
|
+
if packaging_files_config is None:
|
|
158
|
+
return None
|
|
159
|
+
# packaging-files-config is either a string or a dict
|
|
160
|
+
# if it's a string, we can split on the comma to get the dict
|
|
161
|
+
# otherwise we just return
|
|
162
|
+
try:
|
|
163
|
+
return {k.strip(): v.strip() for k, v in [i.split(":") for i in packaging_files_config.split("|")]}
|
|
164
|
+
except AttributeError:
|
|
165
|
+
return packaging_files_config
|
|
166
|
+
|
|
167
|
+
@property
|
|
168
|
+
def package_version(self) -> Optional[str]:
|
|
169
|
+
return str(self.options.get("package-version", ""))
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class CodeGenerator(Plugin):
|
|
173
|
+
def __init__(self, *args, **kwargs: Any) -> None:
|
|
174
|
+
super().__init__(*args, **kwargs)
|
|
175
|
+
self.options_retriever = OptionsRetriever(self.options)
|
|
176
|
+
|
|
177
|
+
def _validate_code_model_options(self) -> None:
|
|
178
|
+
if self.options_retriever.builders_visibility not in [
|
|
179
|
+
"public",
|
|
180
|
+
"hidden",
|
|
181
|
+
"embedded",
|
|
182
|
+
]:
|
|
183
|
+
raise ValueError("The value of --builders-visibility must be either 'public', 'hidden', or 'embedded'")
|
|
184
|
+
|
|
185
|
+
if self.options_retriever.original_models_mode not in ["msrest", "dpg", "none"]:
|
|
186
|
+
raise ValueError(
|
|
187
|
+
"--models-mode can only be 'msrest', 'dpg' or 'none'. "
|
|
188
|
+
"Pass in 'msrest' if you want msrest models, or "
|
|
189
|
+
"'none' if you don't want any."
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
if not self.options_retriever.show_operations and self.options_retriever.builders_visibility == "embedded":
|
|
193
|
+
raise ValueError(
|
|
194
|
+
"Can not embed builders without operations. "
|
|
195
|
+
"Either set --show-operations to True, or change the value of --builders-visibility "
|
|
196
|
+
"to 'public' or 'hidden'."
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
if self.options_retriever.basic_setup_py and not self.options_retriever.package_version:
|
|
200
|
+
raise ValueError("--basic-setup-py must be used with --package-version")
|
|
201
|
+
|
|
202
|
+
if self.options_retriever.package_mode and not self.options_retriever.package_version:
|
|
203
|
+
raise ValueError("--package-mode must be used with --package-version")
|
|
204
|
+
|
|
205
|
+
if not self.options_retriever.show_operations and self.options_retriever.combine_operation_files:
|
|
206
|
+
raise ValueError(
|
|
207
|
+
"Can not combine operation files if you are not showing operations. "
|
|
208
|
+
"If you want operation files, pass in flag --show-operations"
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
if self.options_retriever.package_mode:
|
|
212
|
+
if (
|
|
213
|
+
(
|
|
214
|
+
self.options_retriever.package_mode not in TYPESPEC_PACKAGE_MODE
|
|
215
|
+
and self.options_retriever.from_typespec
|
|
216
|
+
)
|
|
217
|
+
or (
|
|
218
|
+
self.options_retriever.package_mode not in VALID_PACKAGE_MODE
|
|
219
|
+
and not self.options_retriever.from_typespec
|
|
220
|
+
)
|
|
221
|
+
) and not Path(self.options_retriever.package_mode).exists():
|
|
222
|
+
raise ValueError(
|
|
223
|
+
f"--package-mode can only be {' or '.join(TYPESPEC_PACKAGE_MODE)} or directory which contains template files" # pylint: disable=line-too-long
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
if self.options_retriever.multiapi and self.options_retriever.version_tolerant:
|
|
227
|
+
raise ValueError(
|
|
228
|
+
"Can not currently generate version tolerant multiapi SDKs. "
|
|
229
|
+
"We are working on creating a new multiapi SDK for version tolerant and it is not available yet."
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
if self.options_retriever.client_side_validation and self.options_retriever.version_tolerant:
|
|
233
|
+
raise ValueError("Can not generate version tolerant with --client-side-validation. ")
|
|
234
|
+
|
|
235
|
+
if not (self.options_retriever.azure_arm or self.options_retriever.version_tolerant):
|
|
236
|
+
_LOGGER.warning(
|
|
237
|
+
"You are generating with options that would not allow the SDK to be shipped as an official Azure SDK. "
|
|
238
|
+
"Please read https://aka.ms/azsdk/dpcodegen for more details."
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
if not self.options_retriever.is_azure_flavor and self.options_retriever.tracing:
|
|
242
|
+
raise ValueError("Can only have tracing turned on for Azure SDKs.")
|
|
243
|
+
|
|
244
|
+
@staticmethod
|
|
245
|
+
def sort_exceptions(yaml_data: Dict[str, Any]) -> None:
|
|
246
|
+
for client in yaml_data["clients"]:
|
|
247
|
+
for group in client.get("operationGroups", []):
|
|
248
|
+
for operation in group.get("operations", []):
|
|
249
|
+
if not operation.get("exceptions"):
|
|
250
|
+
continue
|
|
251
|
+
# sort exceptions by status code, first single status code, then range, then default
|
|
252
|
+
operation["exceptions"] = sorted(
|
|
253
|
+
operation["exceptions"],
|
|
254
|
+
key=lambda x: (
|
|
255
|
+
3
|
|
256
|
+
if x["statusCodes"][0] == "default"
|
|
257
|
+
else (1 if isinstance(x["statusCodes"][0], int) else 2)
|
|
258
|
+
),
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
@staticmethod
|
|
262
|
+
def remove_cloud_errors(yaml_data: Dict[str, Any]) -> None:
|
|
263
|
+
for client in yaml_data["clients"]:
|
|
264
|
+
for group in client.get("operationGroups", []):
|
|
265
|
+
for operation in group.get("operations", []):
|
|
266
|
+
if not operation.get("exceptions"):
|
|
267
|
+
continue
|
|
268
|
+
i = 0
|
|
269
|
+
while i < len(operation["exceptions"]):
|
|
270
|
+
exception = operation["exceptions"][i]
|
|
271
|
+
if (
|
|
272
|
+
exception.get("schema")
|
|
273
|
+
and exception["schema"]["language"]["default"]["name"] == "CloudError"
|
|
274
|
+
):
|
|
275
|
+
del operation["exceptions"][i]
|
|
276
|
+
i -= 1
|
|
277
|
+
i += 1
|
|
278
|
+
if yaml_data.get("schemas") and yaml_data["schemas"].get("objects"):
|
|
279
|
+
for i in range(len(yaml_data["schemas"]["objects"])):
|
|
280
|
+
obj_schema = yaml_data["schemas"]["objects"][i]
|
|
281
|
+
if obj_schema["language"]["default"]["name"] == "CloudError":
|
|
282
|
+
del yaml_data["schemas"]["objects"][i]
|
|
283
|
+
break
|
|
284
|
+
|
|
285
|
+
def _build_code_model_options(self) -> Dict[str, Any]:
|
|
286
|
+
flags = [
|
|
287
|
+
"azure_arm",
|
|
288
|
+
"head_as_boolean",
|
|
289
|
+
"license_header",
|
|
290
|
+
"keep_version_file",
|
|
291
|
+
"no_async",
|
|
292
|
+
"no_namespace_folders",
|
|
293
|
+
"basic_setup_py",
|
|
294
|
+
"package_name",
|
|
295
|
+
"package_version",
|
|
296
|
+
"client_side_validation",
|
|
297
|
+
"tracing",
|
|
298
|
+
"multiapi",
|
|
299
|
+
"polymorphic_examples",
|
|
300
|
+
"models_mode",
|
|
301
|
+
"builders_visibility",
|
|
302
|
+
"show_operations",
|
|
303
|
+
"show_send_request",
|
|
304
|
+
"only_path_and_body_params_positional",
|
|
305
|
+
"version_tolerant",
|
|
306
|
+
"low_level_client",
|
|
307
|
+
"combine_operation_files",
|
|
308
|
+
"package_mode",
|
|
309
|
+
"package_pprint_name",
|
|
310
|
+
"packaging_files_config",
|
|
311
|
+
"default_optional_constants_to_none",
|
|
312
|
+
"generate_sample",
|
|
313
|
+
"generate_test",
|
|
314
|
+
"default_api_version",
|
|
315
|
+
"from_typespec",
|
|
316
|
+
"flavor",
|
|
317
|
+
"company_name",
|
|
318
|
+
"emit_cross_language_definition_file",
|
|
319
|
+
]
|
|
320
|
+
return {f: getattr(self.options_retriever, f) for f in flags}
|
|
321
|
+
|
|
322
|
+
def get_yaml(self) -> Dict[str, Any]:
|
|
323
|
+
# cadl file doesn't have to be relative to output folder
|
|
324
|
+
with open(self.options["cadl_file"], "r", encoding="utf-8-sig") as fd:
|
|
325
|
+
return yaml.safe_load(fd.read())
|
|
326
|
+
|
|
327
|
+
def get_serializer(self, code_model: CodeModel):
|
|
328
|
+
return JinjaSerializer(code_model, output_folder=self.output_folder)
|
|
329
|
+
|
|
330
|
+
def process(self) -> bool:
|
|
331
|
+
# List the input file, should be only one
|
|
332
|
+
self._validate_code_model_options()
|
|
333
|
+
options = self._build_code_model_options()
|
|
334
|
+
yaml_data = self.get_yaml()
|
|
335
|
+
|
|
336
|
+
self.sort_exceptions(yaml_data)
|
|
337
|
+
|
|
338
|
+
if self.options_retriever.azure_arm:
|
|
339
|
+
self.remove_cloud_errors(yaml_data)
|
|
340
|
+
|
|
341
|
+
code_model = CodeModel(yaml_data=yaml_data, options=options)
|
|
342
|
+
if not self.options_retriever.is_azure_flavor and any(client.lro_operations for client in code_model.clients):
|
|
343
|
+
raise ValueError("Only support LROs for Azure SDKs")
|
|
344
|
+
serializer = self.get_serializer(code_model)
|
|
345
|
+
serializer.serialize()
|
|
346
|
+
|
|
347
|
+
return True
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
if __name__ == "__main__":
|
|
351
|
+
# CADL pipeline will call this
|
|
352
|
+
parsed_args, unknown_args = parse_args()
|
|
353
|
+
CodeGenerator(
|
|
354
|
+
output_folder=parsed_args.output_folder,
|
|
355
|
+
cadl_file=parsed_args.cadl_file,
|
|
356
|
+
**unknown_args,
|
|
357
|
+
).process()
|