From e560846e97e8802427459ad3c0439ed2b203d6da Mon Sep 17 00:00:00 2001 From: peefy Date: Mon, 15 Jul 2024 12:43:54 +0800 Subject: [PATCH] feat: add all python API tests and reference docs Signed-off-by: peefy --- python/README.md | 687 +++++++++++++++++- python/kcl_lib/api/service.py | 557 ++++++++++++++ python/tests/api_test.py | 117 ++- python/tests/test_data/override_file/main.bak | 6 + python/tests/test_data/override_file/main.k | 5 + 5 files changed, 1368 insertions(+), 4 deletions(-) create mode 100644 python/tests/test_data/override_file/main.bak create mode 100644 python/tests/test_data/override_file/main.k diff --git a/python/README.md b/python/README.md index 4306c4f..23f87ee 100644 --- a/python/README.md +++ b/python/README.md @@ -11,7 +11,7 @@ python3 -m pip install kcl-lib ```python import kcl_lib.api as api -args = api.ExecProgram_Args(k_filename_list=["./tests/test_data/schema.k"]) +args = api.ExecProgram_Args(k_filename_list=["/path/to/kcl_file.k"]) api = api.API() result = api.exec_program(args) print(result.yaml_result) @@ -48,3 +48,688 @@ Test ```shell python3 -m pytest ``` + +## API Reference + +### exec_program + +Execute KCL file with arguments and return the JSON/YAML result. + +
Example +

+ +The content of `schema.k` is + +```python +schema AppConfig: + replicas: int + +app: AppConfig { + replicas: 2 +} +``` + +Python Code + +```python +import kcl_lib.api as api + +args = api.ExecProgram_Args(k_filename_list=["schema.k"]) +api = api.API() +result = api.exec_program(args) +assert result.yaml_result == "app:\n replicas: 2" +``` + +

+
+ +A case with the file not found error + +
Example +

+ +```python +import kcl_lib.api as api + +try: + args = api.ExecProgram_Args(k_filename_list=["file_not_found"]) + api = api.API() + result = api.exec_program(args) + assert False +except Exception as err: + assert "Cannot find the kcl file" in str(err) +``` + +

+
+ +### parse_program + +Parse KCL program with entry files and return the AST JSON string. + +
Example +

+ +The content of `schema.k` is + +```python +schema AppConfig: + replicas: int + +app: AppConfig { + replicas: 2 +} +``` + +Python Code + +```python +import kcl_lib.api as api + +args = api.ParseProgram_Args(paths=["schema.k"]) +api = api.API() +result = api.parse_program(args) +assert len(result.paths) == 1 +assert len(result.errors) == 0 +``` + +

+
+ +### parse_file + +Parse KCL single file to Module AST JSON string with import dependencies and parse errors. + +
Example +

+ +The content of `schema.k` is + +```python +schema AppConfig: + replicas: int + +app: AppConfig { + replicas: 2 +} +``` + +Python Code + +```python +import kcl_lib.api as api + +args = api.ParseProgram_Args(paths=[TEST_FILE]) +api = api.API() +result = api.parse_program(args) +assert len(result.paths) == 1 +assert len(result.errors) == 0 +``` + +

+
+ +### parse_program + +Parse KCL program with entry files and return the AST JSON string. + +
Example +

+ +The content of `schema.k` is + +```python +schema AppConfig: + replicas: int + +app: AppConfig { + replicas: 2 +} +``` + +Python Code + +```python +import kcl_lib.api as api + +args = api.ParseProgram_Args(paths=["schema.k"]) +api = api.API() +result = api.parse_program(args) +assert len(result.paths) == 1 +assert len(result.errors) == 0 +``` + +

+
+ +### load_package + +load_package provides users with the ability to parse KCL program and semantic model information including symbols, types, definitions, etc. + +
Example +

+ +The content of `schema.k` is + +```python +schema AppConfig: + replicas: int + +app: AppConfig { + replicas: 2 +} +``` + +Python Code + +```python +import kcl_lib.api as api + +args = api.LoadPackage_Args( + parse_args=api.ParseProgram_Args(paths=["schema.k"]), resolve_ast=True +) +api = api.API() +result = api.load_package(args) +assert list(result.symbols.values())[0].ty.schema_name == "AppConfig" +``` + +

+
+ +### list_variable + +list_variables provides users with the ability to parse KCL program and get all variables by specs. + +
Example +

+ +The content of `schema.k` is + +```python +schema AppConfig: + replicas: int + +app: AppConfig { + replicas: 2 +} +``` + +Python Code + +```python +import kcl_lib.api as api + +args = api.ListVariables_Args(files=[TEST_FILE]) +api = api.API() +result = api.list_variables(args) +assert result.variables["app"].variables[0].value == "AppConfig {replicas: 2}" +``` + +

+
+ +### list_options + +list_options provides users with the ability to parse KCL program and get all option information. + +
Example +

+ +The content of `options.k` is + +```python +a = option("key1") +b = option("key2", required=True) +c = { + metadata.key = option("metadata-key") +} +``` + +Python Code + +```python +import kcl_lib.api as api + +args = api.ParseProgram_Args(paths=["options.k"]) +api = api.API() +result = api.list_options(args) +assert len(result.options) == 3 +assert result.options[0].name == "key1" +assert result.options[1].name == "key2" +assert result.options[2].name == "metadata-key" +``` + +

+
+ +### get_schema_type_mapping + +Get schema type mapping defined in the program. + +
Example +

+ +The content of `schema.k` is + +```python +schema AppConfig: + replicas: int + +app: AppConfig { + replicas: 2 +} +``` + +Python Code + +```python +import kcl_lib.api as api + +exec_args = api.ExecProgram_Args(k_filename_list=["schema.k"]) +args = api.GetSchemaTypeMapping_Args(exec_args=exec_args) +api = api.API() +result = api.get_schema_type_mapping(args) +assert result.schema_type_mapping["app"].properties["replicas"].type == "int" +``` + +

+
+ +### override_file + +Override KCL file with arguments. See [https://www.kcl-lang.io/docs/user_docs/guides/automation](https://www.kcl-lang.io/docs/user_docs/guides/automation) for more override spec guide. + +
Example +

+ +The content of `main.k` is + +```python +a = 1 + +b = { + "a": 1 + "b": 2 +} +``` + +Python Code + +```python +import kcl_lib.api as api +import pathlib + +test_file = "main.k" +args = api.OverrideFile_Args( + file=test_file, + specs=["b.a=2"], +) +api = api.API() +result = api.override_file(args) +assert len(result.parse_errors) == 0 +assert result.result == True +assert pathlib.Path(test_file).read_text() == """\ +a = 1 +b = { + "a": 2 + "b": 2 +} +""" +``` + +

+
+ +### format_code + +Format the code source. + +
Example +

+ +Python Code + +```python +import kcl_lib.api as api + +source_code = """\ +schema Person: + name: str + age: int + + check: + 0 < age < 120 +""" +args = api.FormatCode_Args(source=source_code) +api_instance = api.API() +result = api_instance.format_code(args) +assert ( + result.formatted.decode() + == """\ +schema Person: + name: str + age: int + + check: + 0 < age < 120 + +""" + ) +``` + +

+
+ +### format_path + +Format KCL file or directory path contains KCL files and returns the changed file paths. + +
Example +

+ +The content of `format_path.k` is + +```python +schema Person: + name: str + age: int + + check: + 0 < age < 120 +``` + +Python Code + +```python +import kcl_lib.api as api + +args = api.FormatPath_Args(path="format_path.k") +api_instance = api.API() +result = api_instance.format_path(args) +print(result) +``` + +

+
+ +### lint_path + +Lint files and return error messages including errors and warnings. + +
Example +

+ +The content of `lint_path.k` is + +```python +import math + +a = 1 +``` + +Python Code + +```python +import kcl_lib.api as api + +args = api.LintPath_Args(paths=["lint_path.k"]) +api_instance = api.API() +result = api_instance.lint_path(args) +``` + +

+
+ +### validate_code + +Validate code using schema and JSON/YAML data strings. + +
Example +

+ +Python Code + +```python +import kcl_lib.api as api + +code = """\ +schema Person: + name: str + age: int + + check: + 0 < age < 120 +""" +data = '{"name": "Alice", "age": 10}' +args = api.ValidateCode_Args(code=code, data=data, format="json") +api_instance = api.API() +result = api_instance.validate_code(args) +assert result.success == True +assert result.err_message == "" +``` + +

+
+ +### rename + +Rename all the occurrences of the target symbol in the files. This API will rewrite files if they contain symbols to be renamed. Return the file paths that got changed. + +
Example +

+ +The content of `main.k` is + +```python +a = 1 +b = a +``` + +Python Code + +```python +import kcl_lib.api as api + +args = api.Rename_Args( + package_root=".", + symbol_path="a", + file_paths=["main.k"], + new_name="a2", +) +api_instance = api.API() +result = api_instance.rename(args) +``` + +

+
+ +### rename_code + +Rename all the occurrences of the target symbol and return the modified code if any code has been changed. This API won't rewrite files but return the changed code. + +
Example +

+ +Python Code + +```python +import kcl_lib.api as api + +args = api.RenameCode_Args( + package_root="/mock/path", + symbol_path="a", + source_codes={"/mock/path/main.k": "a = 1\nb = a"}, + new_name="a2", +) +api_instance = api.API() +result = api_instance.rename_code(args) +assert result.changed_codes["/mock/path/main.k"] == "a2 = 1\nb = a2" +``` + +

+
+ +### test + +Test KCL packages with test arguments. + +
Example +

+ +Python Code + +```python +import kcl_lib.api as api +args = api.Test_Args( + pkg_list=["path/to/testing/pkg/..."], +) +api_instance = api.API() +result = api_instance.test(args) +``` + +

+
+ +### load_settings_files + +Load the setting file config defined in `kcl.yaml` + +
Example +

+ +The content of `kcl.yaml` is + +```yaml +kcl_cli_configs: + strict_range_check: true +kcl_options: + - key: key + value: value +``` + +Python Code + +```python +import kcl_lib.api as api + +args = api.LoadSettingsFiles_Args( + work_dir=".", files=["kcl.yaml"] +) +api_instance = api.API() +result = api_instance.load_settings_files(args) +assert result.kcl_cli_configs.files == [] +assert result.kcl_cli_configs.strict_range_check == True +assert ( + result.kcl_options[0].key == "key" and result.kcl_options[0].value == '"value"' +) +``` + +

+
+ +### update_dependencies + +Download and update dependencies defined in the `kcl.mod` file and return the external package name and location list. + +
Example +

+ +The content of `module/kcl.mod` is + +```yaml +[package] +name = "mod_update" +edition = "0.0.1" +version = "0.0.1" + +[dependencies] +helloworld = { oci = "oci://ghcr.io/kcl-lang/helloworld", tag = "0.1.0" } +flask = { git = "https://github.com/kcl-lang/flask-demo-kcl-manifests", commit = "ade147b" } +``` + +Python Code + +```python +import kcl_lib.api as api + +args = api.UpdateDependencies_Args( + manifest_path="module" +) +api_instance = api.API() +result = api_instance.update_dependencies(args) +pkg_names = [pkg.pkg_name for pkg in result.external_pkgs] +assert len(pkg_names) == 2 +assert "helloworld" in pkg_names +assert "flask" in pkg_names +``` + +

+
+ +Call `exec_program` with external dependencies + +
Example +

+ +The content of `module/kcl.mod` is + +```yaml +[package] +name = "mod_update" +edition = "0.0.1" +version = "0.0.1" + +[dependencies] +helloworld = { oci = "oci://ghcr.io/kcl-lang/helloworld", tag = "0.1.0" } +flask = { git = "https://github.com/kcl-lang/flask-demo-kcl-manifests", commit = "ade147b" } +``` + +The content of `module/main.k` is + +```python +import helloworld +import flask + +a = helloworld.The_first_kcl_program +``` + +Python Code + +```python +import kcl_lib.api as api + +args = api.UpdateDependencies_Args( + manifest_path="module" +) +api_instance = api.API() +result = api_instance.update_dependencies(args) +exec_args = api.ExecProgram_Args( + k_filename_list=["module/main.k"], + external_pkgs=result.external_pkgs, +) +result = api_instance.exec_program(exec_args) +assert result.yaml_result == "a: Hello World!" +``` + +

+
+ +### get_version + +Return the KCL service version information. + +
Example +

+ +Python Code + +```python +import kcl_lib.api as api + +api_instance = api.API() +result = api_instance.get_version() +print(result.version_info) +``` + +

+
diff --git a/python/kcl_lib/api/service.py b/python/kcl_lib/api/service.py index 3fe6df6..2958254 100644 --- a/python/kcl_lib/api/service.py +++ b/python/kcl_lib/api/service.py @@ -70,9 +70,75 @@ def ping(self, args: Ping_Args) -> Ping_Result: return self.call("KclvmService.Ping", args) def parse_program(self, args: ParseProgram_Args) -> ParseProgram_Result: + """Parse KCL program with entry files and return the AST JSON string. + + ## Example + + The content of `schema.k` is + + ```python + schema AppConfig: + replicas: int + + app: AppConfig { + replicas: 2 + } + ``` + + Python Code + + ```python + import kcl_lib.api as api + + args = api.ParseProgram_Args(paths=["schema.k"]) + api = api.API() + result = api.parse_program(args) + assert len(result.paths) == 1 + assert len(result.errors) == 0 + ``` + """ return self.call("KclvmService.ParseProgram", args) def exec_program(self, args: ExecProgram_Args) -> ExecProgram_Result: + """Execute KCL file with arguments and return the JSON/YAML result. + + ## Examples + + The content of `schema.k` is + + ```python + schema AppConfig: + replicas: int + + app: AppConfig { + replicas: 2 + } + ``` + + Python Code + + ```python + import kcl_lib.api as api + + args = api.ExecProgram_Args(k_filename_list=["schema.k"]) + api = api.API() + result = api.exec_program(args) + ``` + + A case with the file not found error + + ```python + import kcl_lib.api as api + + try: + args = api.ExecProgram_Args(k_filename_list=["file_not_found"]) + api = api.API() + result = api.exec_program(args) + assert False + except Exception as err: + assert "Cannot find the kcl file" in str(err) + ``` + """ return self.call("KclvmService.ExecProgram", args) def build_program(self, args: BuildProgram_Args) -> BuildProgram_Result: @@ -82,63 +148,554 @@ def exec_artifact(self, args: ExecArtifact_Args) -> ExecProgram_Result: return self.call("KclvmService.ExecArtifact", args) def parse_file(self, args: ParseFile_Args) -> ParseFile_Result: + """Parse KCL single file to Module AST JSON string with import dependencies and parse errors. + + ## Example + + The content of `schema.k` is + + ```python + schema AppConfig: + replicas: int + + app: AppConfig { + replicas: 2 + } + ``` + + Python Code + + ```python + import kcl_lib.api as api + + args = api.ParseProgram_Args(paths=[TEST_FILE]) + api = api.API() + result = api.parse_program(args) + assert len(result.paths) == 1 + assert len(result.errors) == 0 + ``` + """ return self.call("KclvmService.ParseFile", args) def parse_program(self, args: ParseProgram_Args) -> ParseProgram_Result: + """Parse KCL program with entry files and return the AST JSON string. + + ## Example + + The content of `schema.k` is + + ```python + schema AppConfig: + replicas: int + + app: AppConfig { + replicas: 2 + } + ``` + + Python Code + + ```python + import kcl_lib.api as api + + args = api.ParseProgram_Args(paths=["schema.k"]) + api = api.API() + result = api.parse_program(args) + assert len(result.paths) == 1 + assert len(result.errors) == 0 + ``` + """ return self.call("KclvmService.ParseProgram", args) def load_package(self, args: LoadPackage_Args) -> LoadPackage_Result: + """load_package provides users with the ability to parse KCL program and semantic model information including symbols, types, definitions, etc. + + ## Example + + The content of `schema.k` is + + ```python + schema AppConfig: + replicas: int + + app: AppConfig { + replicas: 2 + } + ``` + + Python Code + + ```python + import kcl_lib.api as api + + args = api.LoadPackage_Args( + parse_args=api.ParseProgram_Args(paths=["schema.k"]), resolve_ast=True + ) + api = api.API() + result = api.load_package(args) + assert list(result.symbols.values())[0].ty.schema_name == "AppConfig" + ``` + """ return self.call("KclvmService.LoadPackage", args) def list_options(self, args: ParseProgram_Args) -> ListOptions_Result: + """list_options provides users with the ability to parse KCL program and get all option information. + + ## Example + + The content of `options.k` is + + ```python + a = option("key1") + b = option("key2", required=True) + c = { + metadata.key = option("metadata-key") + } + ``` + + Python Code + + ```python + import kcl_lib.api as api + + args = api.ParseProgram_Args(paths=["options.k"]) + api = api.API() + result = api.list_options(args) + assert len(result.options) == 3 + assert result.options[0].name == "key1" + assert result.options[1].name == "key2" + assert result.options[2].name == "metadata-key" + ``` + """ return self.call("KclvmService.ListOptions", args) def list_variables(self, args: ListVariables_Args) -> ListVariables_Result: + """list_variables provides users with the ability to parse KCL program and get all variables by specs. + + ## Example + + The content of `schema.k` is + + ```python + schema AppConfig: + replicas: int + + app: AppConfig { + replicas: 2 + } + ``` + + Python Code + + ```python + import kcl_lib.api as api + + args = api.ListVariables_Args(files=[TEST_FILE]) + api = api.API() + result = api.list_variables(args) + assert result.variables["app"].variables[0].value == "AppConfig {replicas: 2}" + ``` + """ return self.call("KclvmService.ListVariables", args) def format_code(self, args: FormatCode_Args) -> FormatCode_Result: + """Format the code source. + +
Example +

+ + Python Code + + ```python + import kcl_lib.api as api + + source_code = \"\"\"schema Person: + name: str + age: int + + check: + 0 < age < 120 + \"\"\" + args = api.FormatCode_Args(source=source_code) + api_instance = api.API() + result = api_instance.format_code(args) + assert ( + result.formatted.decode() + == \"\"\"schema Person: + name: str + age: int + + check: + 0 < age < 120 + + \"\"\" + ) + ``` + """ return self.call("KclvmService.FormatCode", args) def format_path(self, args: FormatPath_Args) -> FormatPath_Result: + """Format KCL file or directory path contains KCL files and returns the changed file paths. + + ## Example + + The content of `format_path.k` is + + ```python + schema Person: + name: str + age: int + + check: + 0 < age < 120 + ``` + + Python Code + + ```python + import kcl_lib.api as api + + args = api.FormatPath_Args(path="format_path.k") + api_instance = api.API() + result = api_instance.format_path(args) + print(result) + ``` + """ return self.call("KclvmService.FormatPath", args) def lint_path(self, args: LintPath_Args) -> LintPath_Result: + """Lint files and return error messages including errors and warnings. + + ## Example + + The content of `lint_path.k` is + + ```python + import math + + a = 1 + ``` + + Python Code + + ```python + import kcl_lib.api as api + + args = api.LintPath_Args(paths=["lint_path.k"]) + api_instance = api.API() + result = api_instance.lint_path(args) + ``` + """ return self.call("KclvmService.LintPath", args) def override_file(self, args: OverrideFile_Args) -> OverrideFile_Result: + """Override KCL file with arguments. See [https://www.kcl-lang.io/docs/user_docs/guides/automation](https://www.kcl-lang.io/docs/user_docs/guides/automation) for more override spec guide. + + ## Example + + The content of `main.k` is + + ```python + a = 1 + + b = { + "a": 1 + "b": 2 + } + ``` + + Python Code + + ```python + import kcl_lib.api as api + import pathlib + + test_file = "main.k" + args = api.OverrideFile_Args( + file=test_file, + specs=["b.a=2"], + ) + api = api.API() + result = api.override_file(args) + assert len(result.parse_errors) == 0 + assert result.result == True + assert pathlib.Path(test_file).read_text() == \"\"\"a = 1 + b = { + "a": 2 + "b": 2 + } + \"\"\" + ``` + """ return self.call("KclvmService.OverrideFile", args) def get_schema_type_mapping( self, args: GetSchemaTypeMapping_Args, ) -> GetSchemaTypeMapping_Result: + """Get schema type mapping defined in the program. + + ## Example + + The content of `schema.k` is + + ```python + schema AppConfig: + replicas: int + + app: AppConfig { + replicas: 2 + } + ``` + + Python Code + + ```python + import kcl_lib.api as api + + exec_args = api.ExecProgram_Args(k_filename_list=["schema.k"]) + args = api.GetSchemaTypeMapping_Args(exec_args=exec_args) + api = api.API() + result = api.get_schema_type_mapping(args) + assert result.schema_type_mapping["app"].properties["replicas"].type == "int" + ``` + """ return self.call("KclvmService.GetSchemaTypeMapping", args) def validate_code(self, args: ValidateCode_Args) -> ValidateCode_Result: + """Validate code using schema and JSON/YAML data strings. + + ## Example + Python Code + + ```python + import kcl_lib.api as api + + code = \"\"\" + schema Person: + name: str + age: int + + check: + 0 < age < 120 + \"\"\" + data = '{"name": "Alice", "age": 10}' + args = api.ValidateCode_Args(code=code, data=data, format="json") + api_instance = api.API() + result = api_instance.validate_code(args) + assert result.success == True + assert result.err_message == "" + ``` + """ return self.call("KclvmService.ValidateCode", args) def load_settings_files( self, args: LoadSettingsFiles_Args, ) -> LoadSettingsFiles_Result: + """Load the setting file config defined in `kcl.yaml` + + ## Example + + The content of `kcl.yaml` is + + ```yaml + kcl_cli_configs: + strict_range_check: true + kcl_options: + - key: key + value: value + ``` + + Python Code + + ```python + import kcl_lib.api as api + + args = api.LoadSettingsFiles_Args( + work_dir=".", files=["kcl.yaml"] + ) + api_instance = api.API() + result = api_instance.load_settings_files(args) + assert result.kcl_cli_configs.files == [] + assert result.kcl_cli_configs.strict_range_check == True + assert ( + result.kcl_options[0].key == "key" and result.kcl_options[0].value == '"value"' + ) + ``` + """ return self.call("KclvmService.LoadSettingsFiles", args) def rename(self, args: Rename_Args) -> Rename_Result: + """Rename all the occurrences of the target symbol in the files. This API will rewrite files if they contain symbols to be renamed. Return the file paths that got changed. + + ## Example + + The content of `main.k` is + + ```python + a = 1 + b = a + ``` + + Python Code + + ```python + import kcl_lib.api as api + + args = api.Rename_Args( + package_root=".", + symbol_path="a", + file_paths=["main.k"], + new_name="a2", + ) + api_instance = api.API() + result = api_instance.rename(args) + ``` + """ return self.call("KclvmService.Rename", args) def rename_code(self, args: RenameCode_Args) -> RenameCode_Result: + """Rename all the occurrences of the target symbol and return the modified code if any code has been changed. This API won't rewrite files but return the changed code. + + ## Example + + Python Code + + ```python + import kcl_lib.api as api + + args = api.RenameCode_Args( + package_root="/mock/path", + symbol_path="a", + source_codes={"/mock/path/main.k": "a = 1"}, + new_name="a2", + ) + api_instance = api.API() + result = api_instance.rename_code(args) + assert result.changed_codes["/mock/path/main.k"] == "a2 = 1" + ``` + """ return self.call("KclvmService.RenameCode", args) def test(self, args: Test_Args) -> Test_Result: + """Test KCL packages with test arguments. + + ## Example + + Python Code + + ```python + import kcl_lib.api as api + args = api.Test_Args( + pkg_list=["path/to/testing/pkg/..."], + ) + api_instance = api.API() + result = api_instance.test(args) + ``` + """ return self.call("KclvmService.Test", args) def update_dependencies( self, args: UpdateDependencies_Args ) -> UpdateDependencies_Result: + """Download and update dependencies defined in the `kcl.mod` file and return the external package name and location list. + + ## Examples + + The content of `module/kcl.mod` is + + ```yaml + [package] + name = "mod_update" + edition = "0.0.1" + version = "0.0.1" + + [dependencies] + helloworld = { oci = "oci://ghcr.io/kcl-lang/helloworld", tag = "0.1.0" } + flask = { git = "https://github.com/kcl-lang/flask-demo-kcl-manifests", commit = "ade147b" } + ``` + + Python Code + + ```python + import kcl_lib.api as api + + args = api.UpdateDependencies_Args( + manifest_path="module" + ) + api_instance = api.API() + result = api_instance.update_dependencies(args) + pkg_names = [pkg.pkg_name for pkg in result.external_pkgs] + assert len(pkg_names) == 2 + assert "helloworld" in pkg_names + assert "flask" in pkg_names + ``` + + Call `exec_program` with external dependencies + + The content of `module/kcl.mod` is + + ```yaml + [package] + name = "mod_update" + edition = "0.0.1" + version = "0.0.1" + + [dependencies] + helloworld = { oci = "oci://ghcr.io/kcl-lang/helloworld", tag = "0.1.0" } + flask = { git = "https://github.com/kcl-lang/flask-demo-kcl-manifests", commit = "ade147b" } + ``` + + The content of `module/main.k` is + + ```python + import helloworld + import flask + + a = helloworld.The_first_kcl_program + ``` + + Python Code + + ```python + import kcl_lib.api as api + + args = api.UpdateDependencies_Args( + manifest_path="module" + ) + api_instance = api.API() + result = api_instance.update_dependencies(args) + exec_args = api.ExecProgram_Args( + k_filename_list=["module/main.k"], + external_pkgs=result.external_pkgs, + ) + result = api_instance.exec_program(exec_args) + assert result.yaml_result == "a: Hello World!" + ``` + """ return self.call("KclvmService.UpdateDependencies", args) + def get_version(self) -> GetVersion_Result: + """Return the KCL service version information. + + ## Example + + Python Code + + ```python + import kcl_lib.api as api + + api_instance = api.API() + result = api_instance.get_version() + print(result.version_info) + ``` + """ + return self.call("KclvmService.GetVersion", GetVersion_Args()) + # Helper method to perform the call def call(self, name: str, args): + """Call KCL API with the API name and argument protobuf bytes.""" # Serialize arguments using pickle or json args_serialized = args.SerializeToString() diff --git a/python/tests/api_test.py b/python/tests/api_test.py index 438da23..a02e9a8 100644 --- a/python/tests/api_test.py +++ b/python/tests/api_test.py @@ -2,6 +2,7 @@ def test_exec_api(): + """Execute KCL file with arguments and return the JSON/YAML result.""" import kcl_lib.api as api # Call the `exec_program` method with appropriate arguments @@ -13,6 +14,7 @@ def test_exec_api(): def test_exec_api_failed(): + """Execute KCL file with arguments and return the JSON/YAML result.""" import kcl_lib.api as api try: @@ -26,10 +28,35 @@ def test_exec_api_failed(): assert "Cannot find the kcl file" in str(err) +def test_parse_program_api(): + """Parse KCL program with entry files and return the AST JSON string.""" + import kcl_lib.api as api + + args = api.ParseProgram_Args(paths=[TEST_FILE]) + api = api.API() + result = api.parse_program(args) + assert len(result.paths) == 1 + assert len(result.errors) == 0 + + +def test_parse_file_api(): + """Parse KCL single file to Module AST JSON string with import dependencies and parse errors.""" + import kcl_lib.api as api + + args = api.ParseFile_Args(path=TEST_FILE) + api = api.API() + result = api.parse_file(args) + assert len(result.deps) == 0 + assert len(result.errors) == 0 + + def test_load_package_api(): + """load_package provides users with the ability to parse KCL program and semantic model + information including symbols, types, definitions, etc. + """ import kcl_lib.api as api - # Call the `exec_program` method with appropriate arguments + # Call the `load_package` method with appropriate arguments args = api.LoadPackage_Args( parse_args=api.ParseProgram_Args(paths=[TEST_FILE]), resolve_ast=True ) @@ -40,9 +67,10 @@ def test_load_package_api(): def test_list_variable_api(): + """list_variables provides users with the ability to parse KCL program and get all variables by specs.""" import kcl_lib.api as api - # Call the `exec_program` method with appropriate arguments + # Call the `list_variable` method with appropriate arguments args = api.ListVariables_Args(files=[TEST_FILE]) # Usage api = api.API() @@ -50,11 +78,25 @@ def test_list_variable_api(): assert result.variables["app"].variables[0].value == "AppConfig {replicas: 2}" +def test_list_options_api(): + """list_options provides users with the ability to parse KCL program and get all option information.""" + import kcl_lib.api as api + + args = api.ParseProgram_Args(paths=["./tests/test_data/option/main.k"]) + api = api.API() + result = api.list_options(args) + assert len(result.options) == 3 + assert result.options[0].name == "key1" + assert result.options[1].name == "key2" + assert result.options[2].name == "metadata-key" + + def test_get_schema_type_api(): + """Get schema type mapping defined in the program.""" import kcl_lib.api as api exec_args = api.ExecProgram_Args(k_filename_list=[TEST_FILE]) - # Call the `exec_program` method with appropriate arguments + # Call the `get_schema_type_mapping` method with appropriate arguments args = api.GetSchemaTypeMapping_Args(exec_args=exec_args) # Usage api = api.API() @@ -62,7 +104,40 @@ def test_get_schema_type_api(): assert result.schema_type_mapping["app"].properties["replicas"].type == "int" +def test_override_file_api(): + """Override KCL file with arguments. See https://www.kcl-lang.io/docs/user_docs/guides/automation + for more override spec guide. + """ + import kcl_lib.api as api + import pathlib + + bak_file = "./tests/test_data/override_file/main.bak" + test_file = "./tests/test_data/override_file/main.k" + + pathlib.Path(test_file).write_text(pathlib.Path(bak_file).read_text()) + + args = api.OverrideFile_Args( + file=test_file, + specs=["b.a=2"], + ) + api = api.API() + result = api.override_file(args) + assert len(result.parse_errors) == 0 + assert result.result == True + assert ( + pathlib.Path(test_file).read_text() + == """\ +a = 1 +b = { + "a": 2 + "b": 2 +} +""" + ) + + def test_format_code_api(): + """Format the code source.""" import kcl_lib.api as api source_code = """\ @@ -94,6 +169,7 @@ def test_format_code_api(): def test_format_path_api(): + """Format KCL file or directory path contains KCL files and returns the changed file paths.""" import kcl_lib.api as api TEST_PATH = "./tests/test_data/format_path/test.k" @@ -107,6 +183,7 @@ def test_format_path_api(): def test_lint_path_api(): + """Lint files and return error messages including errors and warnings.""" import kcl_lib.api as api TEST_PATH = "./tests/test_data/lint_path/test-lint.k" @@ -120,6 +197,7 @@ def test_lint_path_api(): def test_validate_code_api(): + """Validate code using schema and JSON/YAML data strings.""" import kcl_lib.api as api code = """\ @@ -142,6 +220,9 @@ def test_validate_code_api(): def test_rename_api(): + """Rename all the occurrences of the target symbol in the files. This API will rewrite files if they contain symbols to be renamed. + Return the file paths that got changed. + """ import kcl_lib.api as api import pathlib @@ -163,6 +244,9 @@ def test_rename_api(): def test_rename_code_api(): + """Rename all the occurrences of the target symbol and return the modified code if any code has been changed. + This API won't rewrite files but return the changed code. + """ import kcl_lib.api as api args = api.RenameCode_Args( @@ -178,7 +262,22 @@ def test_rename_code_api(): assert result.changed_codes["/mock/path/main.k"] == "a2 = 1\nb = a2" +def test_testing_api(): + """Test KCL packages with test arguments.""" + import kcl_lib.api as api + + args = api.Test_Args( + pkg_list=["./tests/test_data/testing/..."], + ) + + api_instance = api.API() + result = api_instance.test(args) + + assert len(result.info) == 2 + + def test_load_settings_files_api(): + """Load the setting file config defined in `kcl.yaml`""" import kcl_lib.api as api args = api.LoadSettingsFiles_Args( @@ -196,6 +295,9 @@ def test_load_settings_files_api(): def test_update_dependencies_api(): + """Download and update dependencies defined in the `kcl.mod` file and return the + external package name and location list. + """ import kcl_lib.api as api args = api.UpdateDependencies_Args( @@ -226,3 +328,12 @@ def test_exec_api_with_external_dependencies(): ) result = api_instance.exec_program(exec_args) assert result.yaml_result == "a: Hello World!" + + +def test_get_version_api(): + """Return the KCL service version information.""" + import kcl_lib.api as api + + api_instance = api.API() + result = api_instance.get_version() + assert "Version" in str(result) and "GitCommit" in str(result) diff --git a/python/tests/test_data/override_file/main.bak b/python/tests/test_data/override_file/main.bak new file mode 100644 index 0000000..75191e4 --- /dev/null +++ b/python/tests/test_data/override_file/main.bak @@ -0,0 +1,6 @@ +a = 1 + +b = { + "a": 1 + "b": 2 +} \ No newline at end of file diff --git a/python/tests/test_data/override_file/main.k b/python/tests/test_data/override_file/main.k new file mode 100644 index 0000000..460fcac --- /dev/null +++ b/python/tests/test_data/override_file/main.k @@ -0,0 +1,5 @@ +a = 1 +b = { + "a": 2 + "b": 2 +}