From 3a60807549772d3662d33eb19d35378ddd532383 Mon Sep 17 00:00:00 2001 From: chai2010 Date: Fri, 13 May 2022 18:13:51 +0800 Subject: [PATCH] import code --- .gitignore | 1 + LICENSE | 201 +++++++++++++++++++++++++++++++++ Makefile | 11 ++ README.md | 6 +- VERSION | 1 + a_embed.go | 43 +++++++ go.mod | 5 + hello.go | 14 +++ hello/api.md | 30 +++++ hello/hello_test.k | 37 ++++++ hello/plugin.py | 57 ++++++++++ hello/plugin_test.py | 41 +++++++ plugins.txt | 2 + project_context/api.md | 59 ++++++++++ project_context/plugin.py | 94 +++++++++++++++ project_context/plugin_test.py | 18 +++ 16 files changed, 619 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 VERSION create mode 100644 a_embed.go create mode 100644 go.mod create mode 100644 hello.go create mode 100644 hello/api.md create mode 100644 hello/hello_test.k create mode 100644 hello/plugin.py create mode 100644 hello/plugin_test.py create mode 100644 plugins.txt create mode 100644 project_context/api.md create mode 100644 project_context/plugin.py create mode 100644 project_context/plugin_test.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7c4fb46 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/_kclvm_plugins_ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..989e2c5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6d677f6 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +default: + +kcl-test: + kcl-test ./... + +test: + cd _test && python3 -m pytest + +clean: diff --git a/README.md b/README.md index d32c3cd..043ad07 100644 --- a/README.md +++ b/README.md @@ -1 +1,5 @@ -# kcl-plugin \ No newline at end of file +# KCL 插件 + +KCL 支持通过 Python 语言开发插件,KCL 程序导入插件中的函数。KCL通过插件运行时和辅助的命令行工具提供插件支持。 + +插件文档:https://kusionstack.io/docs/reference/lang/plugin/overview diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..6da28dd --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.1.1 \ No newline at end of file diff --git a/a_embed.go b/a_embed.go new file mode 100644 index 0000000..eb21c5f --- /dev/null +++ b/a_embed.go @@ -0,0 +1,43 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +// kclvm plugins. +package kcl_plugin + +import ( + "embed" + "io/fs" + "os" + "path/filepath" +) + +//go:embed README.md +//go:embed hello +//go:embed project_context +var PluginFS embed.FS + +func InstallPlugins(root string) error { + embedFS := PluginFS + os.MkdirAll(root, 0777) + return fs.WalkDir(embedFS, ".", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() { + return nil + } + abspath := filepath.Join(root, path) + if err := os.MkdirAll(filepath.Dir(abspath), 0777); err != nil { + _ = err + } + + data, err := fs.ReadFile(embedFS, path) + if err != nil { + return err + } + err = os.WriteFile(abspath, data, 0777) + if err != nil { + return err + } + return nil + }) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..d1e8b98 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +module kusionstack.io/kcl-plugin + +go 1.17 diff --git a/hello.go b/hello.go new file mode 100644 index 0000000..4f8ddf3 --- /dev/null +++ b/hello.go @@ -0,0 +1,14 @@ +// Copyright 2021 The KCL Authors. All rights reserved. + +//go:build ignore +// +build ignore + +package main + +import ( + "kusionstack.io/kcl-plugin" +) + +func main() { + kcl_plugin.InstallPlugins("_kclvm_plugins_") +} diff --git a/hello/api.md b/hello/api.md new file mode 100644 index 0000000..a332083 --- /dev/null +++ b/hello/api.md @@ -0,0 +1,30 @@ +# plugin: `hello` - hello doc + +long describe + +*version: 0.0.1* + +## `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 + diff --git a/hello/hello_test.k b/hello/hello_test.k new file mode 100644 index 0000000..333aea4 --- /dev/null +++ b/hello/hello_test.k @@ -0,0 +1,37 @@ +# Copyright 2021 The KCL Authors. All rights reserved. + +import kcl_plugin.hello as hello + +schema TestHello_tolower: + name = hello.tolower('KCL') + assert name == 'kcl', "${name}" + +schema TestHello_add: + v = hello.add(1, 2) + assert v == 3, "${v}" + +schema TestHello_update_dict: + assert hello.update_dict({'name': 123}, 'name', 'kcl')['name'] == 'kcl' + +schema TestHello_list_append: + l = hello.list_append(['abc'], 'name', 123) + assert len(l) == 3 + assert l[0] == 'abc' + assert l[1] == 'name' + assert l[2] == 123 + + +schema TestHello_foo: + v = hello.foo('aaa', 'bbb', x=123, y=234, abcd=1234) + assert len(v) == 5 + assert v['a'] == 'aaa' + assert v['b'] == 'bbb' + assert v['x'] == 123 + assert v['y'] == 234 + assert v['abcd'] == 1234 + + v = hello.foo('aaa', 'bbb', x=123) + assert len(v) == 3 + assert v['a'] == 'aaa' + assert v['b'] == 'bbb' + assert v['x'] == 123 diff --git a/hello/plugin.py b/hello/plugin.py new file mode 100644 index 0000000..6458b79 --- /dev/null +++ b/hello/plugin.py @@ -0,0 +1,57 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +INFO = { + 'name': 'hello', + 'describe': 'hello doc', + 'long_describe': 'long describe', + 'version': '0.0.1', +} + + +global_int: int = 0 + + +def set_global_int(v: int): + global global_int + global_int = v + + +def get_global_int() -> int: + return global_int + + +def say_hello(msg: str): + print('hello.say_hello:', msg) + return None + + +def add(a: int, b: int) -> int: + """add two numbers, and return result""" + return a + b + + +def tolower(s: str) -> str: + return s.lower() + + +def update_dict(d: dict, key: str, value: str) -> dict: + d[key] = value + return d + + +def list_append(l: list, *values) -> list: + for v in values: + l.append(v) + return l + + +def foo(a, b, *, x, **values): + return {'a': a, 'b': b, 'x': x, **values} + + +if __name__ == "__main__": + print(INFO) + x = add(1, 2) + print(x) + + foo('aaa', 'bbb', x=123, y=234) diff --git a/hello/plugin_test.py b/hello/plugin_test.py new file mode 100644 index 0000000..c98de28 --- /dev/null +++ b/hello/plugin_test.py @@ -0,0 +1,41 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +# python3 -m pytest + +import plugin + + +def test_plugin_hello_add(): + assert plugin.add(1, 2) == 3 + + +def test_plugin_hello_tolower(): + assert plugin.tolower('KCL') == 'kcl' + + +def test_plugin_hello_update_dict(): + assert plugin.update_dict({'name': 123}, 'name', 'kcl')['name'] == 'kcl' + + +def test_plugin_hello_list_append(): + l = plugin.list_append(['abc'], 'name', 123) + assert len(l) == 3 + assert l[0] == 'abc' + assert l[1] == 'name' + assert l[2] == 123 + + +def test_plugin_hello_foo(): + v = plugin.foo('aaa', 'bbb', x=123, y=234, abcd=1234) + assert len(v) == 5 + assert v['a'] == 'aaa' + assert v['b'] == 'bbb' + assert v['x'] == 123 + assert v['y'] == 234 + assert v['abcd'] == 1234 + + v = plugin.foo('aaa', 'bbb', x=123) + assert len(v) == 3 + assert v['a'] == 'aaa' + assert v['b'] == 'bbb' + assert v['x'] == 123 diff --git a/plugins.txt b/plugins.txt new file mode 100644 index 0000000..c668445 --- /dev/null +++ b/plugins.txt @@ -0,0 +1,2 @@ +hello +project_context diff --git a/project_context/api.md b/project_context/api.md new file mode 100644 index 0000000..6d3dd1d --- /dev/null +++ b/project_context/api.md @@ -0,0 +1,59 @@ +# plugin: `project_context` - project_context doc + +project_context extract base info from project.yaml&stack.yaml + +*version: 0.0.1* + +## `get_project_current_path` + +return the relative path of first file + +Example: + +``` +import kcl_plugin.project_context as ctx + +path = ctx.get_project_current_path() +print(path) +``` + +## `get_project_input_file` + +return compiling file list + +Example: + +``` +import kcl_plugin.project_context as ctx + +input_file = ctx.get_project_input_file() +print(input_file) +``` + +## `get_project_context` + +return the current project context from project.yaml + +Example: + +``` +import kcl_plugin.project_context as ctx + +project = ctx.get_project_context() +# Get project name +print(project?.name) +``` + +## `get_stack_context` + +return the current stack context from stack.yaml + +Example: + +``` +import kcl_plugin.project_context as ctx + +stack = ctx.get_stack_context() +# Get stack name +print(stack?.name) +``` diff --git a/project_context/plugin.py b/project_context/plugin.py new file mode 100644 index 0000000..94a54e3 --- /dev/null +++ b/project_context/plugin.py @@ -0,0 +1,94 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +import os +import ruamel.yaml as _yaml +from pathlib import Path +from typing import Any, Dict, List + +import kclvm.config as kcfg + +INFO = { + 'name': 'project_context', + 'describe': 'project_context extract base info from project.yaml&stack.yaml', + 'long_describe': 'project_context extract base info from project.yaml&stack.yaml', + 'version': '0.0.1', +} +BASE_PKG_PATH = "base/pkg" +STACK_YAML_FILE = "stack.yaml" +PROJECT_YAML_FILE = "project.yaml" +CRD_DIR_NAME = "crd" + + +def get_project_current_path() -> str: + return kcfg.current_path + + +def get_project_input_file() -> List[str]: + return kcfg.input_file + + +def get_stack_context() -> Dict[str, Any]: + """get_stack_context returns the current stack context""" + stackRoot = find_stack_root() + if stackRoot: + return loadIfExist(stackRoot / STACK_YAML_FILE) + return {} + + +def get_project_context() -> Dict[str, Any]: + """get_project_context returns the current project context""" + projectRoot = find_project_root() + if projectRoot: + return loadIfExist(projectRoot / PROJECT_YAML_FILE) + return {} + + +def get_project_crds() -> List[Any]: + """get_project_crd returns the crd yaml document array in current project dir""" + projectStackRoot = find_stack_root() + if not projectStackRoot: + return [] + + projectProjectRoot = projectStackRoot.parent + projectCrdDir = projectProjectRoot / CRD_DIR_NAME + + crdYamlDocuments = [] + if projectCrdDir.exists(): + for root, dirs, files in sorted(os.walk(projectCrdDir)): + for file in sorted(files): + crdFile = (Path(root) / file) + # safe_load_all() returns a sequence of Python objects corresponding to the documents in the stream + yamlDocuments = _yaml.safe_load_all(crdFile.read_text()) + crdYamlDocuments += list(yamlDocuments) + return crdYamlDocuments + + +def find_stack_root() -> Path: + """find_stack_root returns the root dir path of current project stack""" + if not kcfg.input_file or len(kcfg.input_file) == 0 or not kcfg.current_path or len(kcfg.current_path) == 0: + return None + inputfileDirListForProject = [] + # filter base dir + for inputfile in kcfg.input_file: + if BASE_PKG_PATH not in inputfile: + inputfileDirListForProject.append(Path(os.path.abspath(inputfile)).parent) + # return empty dir if no project input file dir found + if len(inputfileDirListForProject) == 0: + return None + for inputfileDir in inputfileDirListForProject: + # if current dir contains stack.yaml, set current dir as project stack root path + if (inputfileDir / STACK_YAML_FILE).is_file(): + return inputfileDir + return None + + +def find_project_root() -> Path: + """find_project_root returns the root dir path of current project""" + stackRoot = find_stack_root() + return stackRoot.parent if stackRoot else None + + +def loadIfExist(yamlFile: Path) -> Dict[str, Any]: + if yamlFile.is_file(): + return _yaml.safe_load(yamlFile.read_text()) + return {} diff --git a/project_context/plugin_test.py b/project_context/plugin_test.py new file mode 100644 index 0000000..710bd14 --- /dev/null +++ b/project_context/plugin_test.py @@ -0,0 +1,18 @@ +# Copyright 2020 The KCL Authors. All rights reserved. + +# python3 -m pytest + +import plugin + + +def test_get_project_current_path(): + assert plugin.get_project_current_path() == None + +def test_get_project_input_file(): + assert plugin.get_project_input_file() == None + +def test_get_project_context(): + assert plugin.get_project_context() == {} + +def test_get_project_crds(): + assert plugin.get_project_crds() == [] \ No newline at end of file