Skip to content

docs: update kcl python and java documents #395

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions docs/reference/lang/codelab/schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,6 @@ Suppose we have some schema logic, we can wrapper it into schema:
```python
schema Deployment[priority]:
name: str
cpu: int = _cpu
memory: int = _cpu * 2
image: str
service: "my-service" = "my-service"
replica: int = 1
Expand All @@ -181,6 +179,9 @@ schema Deployment[priority]:
_cpu = 1024
else:
_cpu = 2048

cpu: int = _cpu
memory: int = _cpu * 2
```

Now, we can define a config by creating a schema instance and pass in priority as an argument to schema:
Expand Down Expand Up @@ -227,8 +228,6 @@ Now we want to define a detailed schema with service and volumes, we can do it a
```python
schema Deployment[priority]:
name: str
cpu: int = _cpu
memory: int = _cpu * 2
volumes?: [Volume]
image: str
service?: Service
Expand All @@ -245,6 +244,9 @@ schema Deployment[priority]:
else:
_cpu = 2048

cpu: int = _cpu
memory: int = _cpu * 2

schema Port:
name: str
protocol: str
Expand Down Expand Up @@ -373,8 +375,6 @@ import regex

schema Deployment[priority]:
name: str
cpu: int = _cpu
memory: int = _cpu * 2
volumes?: [Volume]
image: str
service?: Service
Expand All @@ -391,6 +391,9 @@ schema Deployment[priority]:
else:
_cpu = 2048

cpu: int = _cpu
memory: int = _cpu * 2

check:
multiplyof(cpu, 256), "cpu must be a multiplier of 256"
regex.match(image, "^[a-zA-Z]+:\d+\.\d+\.\d+$"), "image name should be like 'nginx:1.14.2'"
Expand Down
190 changes: 41 additions & 149 deletions docs/reference/plugin/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,184 +101,76 @@ func TestPluginAdd(t *testing.T) {

### 0. Prerequisites

Using the KCL Python plugin requires the presence of `Python 3.7+` in your `PATH`, install the KCL python SDK and set the plugin path.
Using the KCL Python plugin requires the presence of `Python 3.7+` in your `PATH` and install the KCL python SDK.

```shell
python3 -m pip install kclvm
alias kcl-plugin="python3 -m kclvm.tools.plugin"
export KCL_PLUGINS_ROOT=~/.kcl/plugins
python3 -m pip kcl_lib
```

### 1. Hello Plugin

KCL plugins are installed in the `plugins` subdirectory of KCL (usually installed in the `$HOME/.kcl/plugins` directory), or set through the `$KCL_PLUGINS_ROOT` environment variable. Besides, the `plugins` directory could also be placed at the `pwd` path. KCL plugins are managed in the Git repository: [https://github.com/kcl-lang/kcl-plugin](https://github.com/kcl-lang/kcl-plugin), we can clone the repository for development.

Enter the `kcl-plugin info` command to view the plugin directory (replace `/Users/kcl_user` with the local `$HOME` path):

```shell
$ kcl-plugin info
# plugin_root: /Users/kcl_user/.kcl/plugins
```

View the list of plugins with the `kcl-plugin list` subcommand:

```shell
$ kcl-plugin list
hello: hello doc - 0.0.1
```

Where `hello` is an example builtin plugin (do not modify the plugin).

In KCL code, the `hello` plugin can be imported via `import kcl_plugin.hello`. `main.k` code is as follows:
Write the following Python code and add the the plugin named `my_plugin`.

```python
import kcl_plugin.hello
import kcl_lib.plugin as plugin
import kcl_lib.api as api
name = "kcl"
three = hello.add(1,2)
```
plugin.register_plugin("my_plugin", {"add": lambda x, y: x + y})
The output result is
def main():
result = api.API().exec_program(
api.ExecProgram_Args(k_filename_list=["test.k"])
)
assert result.yaml_result == "result: 2"
```shell
$ python3 -m kclvm main.k
name: kcl
three: 3
main()
```

### 2. `kcl-plugin` Command
The content of `test.k` are:

`kcl-plugin` is a plugin helper command line tool, the command line help is as follows:
```python
import kcl_plugin.my_plugin
```shell
$ kcl-plugin
usage: kcl-plugin [-h] {list,info,init,gendoc,test} ...
positional arguments:
{list,info,init,gendoc,test}
kcl plugin sub commands
list list all plugins
info show plugin document
init init a new plugin
gendoc gen all plugins document
test test plugin
optional arguments:
-h, --help show this help message and exit
result = my_plugin.add(1, 1)
```

- The `list` subcommand is used to view the list of plugins.
- The `info` subcommand is used to view the plugin directory and information about each plugin.
- The `init` subcommand is used to initialize new plugins.
- The `gendoc` subcommand is used to update the API documentation of all plugins.
- The `test` subcommand is used to test specified plugins.
## Use Java to Write Plugins

### 3. Plugin Information and Documentation
### 0. Prerequisites

Enter `kcl-plugin info hello` to view the `hello` plugin information:
Using the KCL Java plugin requires the presence of `Java 8+` in your `PATH` and install the KCL Java SDK.

```shell
$ kcl-plugin info hello
{
"name": "hello",
"describe": "hello doc",
"long_describe": "long describe",
"version": "0.0.1",
"method": {
"add": "add two numbers, and return result",
"foo": "no doc",
"list_append": "no doc",
"say_hello": "no doc",
"tolower": "no doc",
"update_dict": "no doc"
}
}
```
### 1. Hello Plugin

The information of the plugin mainly includes the name and version information of the plugin, and the function information provided by the plugin. This information is consistent with the automatically generated `api.md` file in the plugin directory (regenerate the `api.md` file for all plugins via `kcl-plugin gendoc` when the plugin API document changes).
Write the following Java code and add the the plugin named `my_plugin`.

### 4. Plugin Directory Structure
```java
package com.kcl;
The directory structure of the plugin is as follows (replace `/Users/kcl_user` with the local `$HOME` path):
import com.kcl.api.API;
import com.kcl.api.Spec.ExecProgram_Args;
import com.kcl.api.Spec.ExecProgram_Result;
```shell
$ tree /Users/kcl_user/.kcl/plugins/
/Users/kcl_user/.kcl/plugins/
├── _examples
├── _test
└── hello
├── api.md
├── plugin.py
└── plugin_test.py
$
```
import java.util.Collections;
The `_examples` directory is the sample code of the plugin, the `_test` directory is the KCL test code of the plugin, and the other directories starting with letters are ordinary plugins. The content of the plugin is as follows:
public class PluginTest {
public static void main(String[] mainArgs) throws Exception {
API.registerPlugin("my_plugin", Collections.singletonMap("add", (args, kwArgs) -> {
return (int) args[0] + (int) args[1];
}));
API api = new API();
```shell
$ cat ./hello/plugin.py
# Copyright 2020 The KCL Authors. All rights reserved.
INFO = {
'name': 'hello',
'describe': 'hello doc',
'long_describe': 'long describe',
'version': '0.0.1',
ExecProgram_Result result = api
.execProgram(ExecProgram_Args.newBuilder().addKFilenameList("test.k").build());
System.out.println(result.getYamlResult());
}
}
def add(a: int, b: int) -> int:
"""add two numbers, and return result"""
return a + b
...
```

Where `INFO` specifies the name of the plugin, a brief description, a detailed description and version information. And all the functions whose names start with letters are the functions provided by the plugin, so the `add` function can be called directly in KCL.

> Note: KCL plugins are implemented in an independent pure Python code file, and plugins cannot directly call each other.

### 5. Create Plugin

A plugin can be created with the `kcl-plugin init` command:

```
$ kcl-plugin init hi
$ kcl-plugin list
hello: hello doc - 0.0.1
hi: hi doc - 0.0.1
```

The `kcl-plugin init` command will construct a new plugin from the built-in template, and then we can view the created plugin information with the `kcl-plugin list` command.

### 6. Remove Plugin

KCL plugins are located in the `plugins` subdirectory of KCL (usually installed in the `$HOME/.kcl/plugins` directory).
We can query the plugin installation directory with the command `kcl-plugin info`.

```shell
$ kcl-plugin info
/Users/kcl_user/.kcl/plugins/
$ tree /Users/kcl_user/.kcl/plugins/
/Users/kcl_user/.kcl/plugins/
├── _examples
├── _test
└── hello -- Delete this directory to delete the hello plugin
├── api.md
├── plugin.py
└── plugin_test.py
$
```
The content of `test.k` are:

### 7. Test Plugin

There is a `plugin_test.py` file in the plugin directory, which is the unit test file of the plugin (based on the `pytest` testing framework). Also placed in the `_test` directory are plugin integration tests for KCL files. The `plugin_test.py` unit test is required, and the KCL integration tests in the `_test` directory can be added as needed.

Unit tests for plugins can be executed via `kcl-plugin test`:
```python
import kcl_plugin.my_plugin
```shell
$ kcl-plugin test hello
============================= test session starts ==============================
platform darwin -- Python 3.7.6+, pytest-5.3.5, py-1.9.0, pluggy-0.13.1
rootdir: /Users/kcl_user
collected 5 items
.kcl/plugins/hello/plugin_test.py ..... [100%]
============================== 5 passed in 0.03s ===============================
$
result = my_plugin.add(1, 1)
```

Integration tests can be tested by executing the `python3 -m pytest` command in the `_test` directory.
5 changes: 3 additions & 2 deletions examples/codelab/schema/my_config.k
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import regex

schema Deployment[priority]:
name: str
cpu: int = _cpu
memory: int = _cpu * 2
volumes?: [Volume]
image: str
service?: Service
Expand All @@ -20,6 +18,9 @@ schema Deployment[priority]:
else:
_cpu = 2048

cpu: int = _cpu
memory: int = _cpu * 2

check:
multiplyof(cpu, 256), "cpu must be a multiplier of 256"
regex.match(image, "^[a-zA-Z]+:\d+\.\d+\.\d+$"), "image name should be like 'nginx:1.14.2'"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,6 @@ nginx = Deployment {
```python
schema Deployment[priority]:
name: str
cpu: int = _cpu
memory: int = _cpu * 2
image: str
service: "my-service" = "my-service"
replica: int = 1
Expand All @@ -181,6 +179,9 @@ schema Deployment[priority]:
_cpu = 1024
else:
_cpu = 2048

cpu: int = _cpu
memory: int = _cpu * 2
```

现在,我们可以通过创建 schema 实例来定义配置,并将优先级作为参数传递给模式:
Expand Down Expand Up @@ -227,8 +228,6 @@ nginx:
```python
schema Deployment[priority]:
name: str
cpu: int = _cpu
memory: int = _cpu * 2
volumes?: [Volume]
image: str
service?: Service
Expand All @@ -244,6 +243,9 @@ schema Deployment[priority]:
_cpu = 1024
else:
_cpu = 2048

cpu: int = _cpu
memory: int = _cpu * 2

schema Port:
name: str
Expand Down Expand Up @@ -373,8 +375,6 @@ import regex

schema Deployment[priority]:
name: str
cpu: int = _cpu
memory: int = _cpu * 2
volumes?: [Volume]
image: str
service?: Service
Expand All @@ -391,6 +391,9 @@ schema Deployment[priority]:
else:
_cpu = 2048

cpu: int = _cpu
memory: int = _cpu * 2

check:
multiplyof(cpu, 256), "cpu must be a multiplier of 256"
regex.match(image, "^[a-zA-Z]+:\d+\.\d+\.\d+$"), "image name should be like 'nginx:1.14.2'"
Expand Down
Loading
Loading