From 39d1d0dc4649f44bcd9676fd318d3dc4814d52c7 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Tue, 14 Jun 2022 22:40:59 +0800 Subject: [PATCH] fix: filter commented out functions when generating plugin file --- docs/CHANGELOG.md | 1 + examples/demo-with-py-plugin/debugtalk.py | 5 + hrp/build.go | 142 ++++++++++-------- hrp/build_test.go | 84 +++++++++++ .../scaffold/templates/plugin/debugtalk.py | 5 + 5 files changed, 177 insertions(+), 60 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index a50c346a..d45da9c7 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -3,6 +3,7 @@ ## v4.1.4 (2022-06-14) - feat: config pypi index url by setting environment `PYPI_INDEX_URL` +- fix: filter commented out functions when generating plugin file ## v4.1.3 (2022-06-14) diff --git a/examples/demo-with-py-plugin/debugtalk.py b/examples/demo-with-py-plugin/debugtalk.py index ea48ff48..334a46c7 100644 --- a/examples/demo-with-py-plugin/debugtalk.py +++ b/examples/demo-with-py-plugin/debugtalk.py @@ -3,6 +3,11 @@ import time from typing import List +# commented out function will be filtered +# def get_headers(): +# return {"User-Agent": "hrp"} + + def get_user_agent(): return "hrp/funppy" diff --git a/hrp/build.go b/hrp/build.go index 6c97a207..81682dfd 100644 --- a/hrp/build.go +++ b/hrp/build.go @@ -26,36 +26,54 @@ var pyTemplate string var goTemplate string // regex for finding all function names -var ( - regexPyFunctionName = regexp.MustCompile(`def ([a-zA-Z_]\w*)\(.*\)`) - regexGoFunctionName = regexp.MustCompile(`func ([A-Z][a-zA-Z_]\w*)\(.*\)`) -) - -type pluginTemplateContent struct { - Version string // hrp version - FunctionNames []string // function names +type regexFunctions struct { + *regexp.Regexp } -func findAllFunctionNames(t *regexp.Regexp, path string) (functionNames []string, err error) { - log.Info().Str("path", path).Msg("find all function names from plugin file") - - content, err := os.ReadFile(path) - if err != nil { - log.Error().Err(err).Msg("failed to read file") - return nil, errors.Wrap(err, "read file failed") - } +var ( + regexPyFunctionName = regexFunctions{regexp.MustCompile(`(?m)^def ([a-zA-Z_]\w*)\(.*\)`)} + regexGoFunctionName = regexFunctions{regexp.MustCompile(`(?m)^func ([a-zA-Z_]\w*)\(.*\)`)} +) +func (r *regexFunctions) findAllFunctionNames(content string) []string { + var functionNames []string // find all function names - functionNameSlice := t.FindAllStringSubmatch(string(content), -1) + functionNameSlice := r.FindAllStringSubmatch(content, -1) for _, elem := range functionNameSlice { name := strings.Trim(elem[1], " ") functionNames = append(functionNames, name) } - return + var filteredFunctionNames []string + if r == ®exPyFunctionName { + // filter private functions + for _, name := range functionNames { + if strings.HasPrefix(name, "__") { + continue + } + filteredFunctionNames = append(filteredFunctionNames, name) + } + } else if r == ®exGoFunctionName { + // filter main and init function + for _, name := range functionNames { + if name == "main" || name == "init" { + continue + } + filteredFunctionNames = append(filteredFunctionNames, name) + } + } + + log.Info().Strs("functionNames", filteredFunctionNames).Msg("find all function names") + return filteredFunctionNames } -func generate(data *pluginTemplateContent, tmpl, output string) error { +type pluginTemplate struct { + path string // file path + Version string // hrp version + FunctionNames []string // function names +} + +func (pt *pluginTemplate) generate(tmpl, output string) error { file, err := os.Create(output) if err != nil { log.Error().Err(err).Msg("open output file failed") @@ -64,7 +82,7 @@ func generate(data *pluginTemplateContent, tmpl, output string) error { defer file.Close() writer := bufio.NewWriter(file) - err = template.Must(template.New("debugtalk").Parse(tmpl)).Execute(writer, data) + err = template.Must(template.New("debugtalk").Parse(tmpl)).Execute(writer, pt) if err != nil { log.Error().Err(err).Msg("execute template parsing failed") return err @@ -79,28 +97,28 @@ func generate(data *pluginTemplateContent, tmpl, output string) error { return err } -// buildGo builds debugtalk.go to debugtalk.bin -func buildGo(path string, output string) error { - functionNames, err := findAllFunctionNames(regexGoFunctionName, path) +func (pt *pluginTemplate) generatePy(output string) error { + // specify output file path + if output == "" { + dir, _ := os.Getwd() + output = filepath.Join(dir, PluginPySourceGenFile) + } else if builtin.IsFolderPathExists(output) { + output = filepath.Join(output, PluginPySourceGenFile) + } + + // generate .debugtalk_gen.py + err := pt.generate(pyTemplate, output) if err != nil { - return errors.Wrap(err, "find all function names failed") - } - // filter main and init function - var filteredFunctionNames []string - for _, name := range functionNames { - if name == "main" || name == "init" { - continue - } - filteredFunctionNames = append(filteredFunctionNames, name) + return err } - templateContent := &pluginTemplateContent{ - Version: version.VERSION, - FunctionNames: filteredFunctionNames, - } + log.Info().Str("output", output).Str("plugin", pt.path).Msg("build python plugin successfully") + return nil +} - pluginDir := filepath.Dir(path) - err = generate(templateContent, goTemplate, filepath.Join(pluginDir, PluginGoSourceGenFile)) +func (pt *pluginTemplate) generateGo(output string) error { + pluginDir := filepath.Dir(pt.path) + err := pt.generate(goTemplate, filepath.Join(pluginDir, PluginGoSourceGenFile)) if err != nil { return errors.Wrap(err, "generate hashicorp plugin failed") } @@ -139,14 +157,30 @@ func buildGo(path string, output string) error { outputPath, _ := filepath.Abs(output) // build go plugin to debugtalk.bin - cmd := exec.Command("go", "build", "-o", outputPath, PluginGoSourceGenFile, filepath.Base(path)) + cmd := exec.Command("go", "build", "-o", outputPath, PluginGoSourceGenFile, filepath.Base(pt.path)) if err := builtin.ExecCommandInDir(cmd, pluginDir); err != nil { return errors.Wrap(err, "go build plugin failed") } - log.Info().Str("output", outputPath).Str("plugin", path).Msg("build go plugin successfully") + log.Info().Str("output", outputPath).Str("plugin", pt.path).Msg("build go plugin successfully") return nil } +// buildGo builds debugtalk.go to debugtalk.bin +func buildGo(path string, output string) error { + content, err := os.ReadFile(path) + if err != nil { + log.Error().Err(err).Msg("failed to read file") + return errors.Wrap(err, "read file failed") + } + functionNames := regexGoFunctionName.findAllFunctionNames(string(content)) + + templateContent := &pluginTemplate{ + Version: version.VERSION, + FunctionNames: functionNames, + } + return templateContent.generateGo(output) +} + // buildPy completes funppy information in debugtalk.py func buildPy(path string, output string) error { // check the syntax of debugtalk.py @@ -155,31 +189,19 @@ func buildPy(path string, output string) error { return errors.Wrap(err, "python plugin syntax invalid") } - functionNames, err := findAllFunctionNames(regexPyFunctionName, path) + content, err := os.ReadFile(path) if err != nil { - return err + log.Error().Err(err).Msg("failed to read file") + return errors.Wrap(err, "read file failed") } - templateContent := &pluginTemplateContent{ + functionNames := regexPyFunctionName.findAllFunctionNames(string(content)) + + templateContent := &pluginTemplate{ + path: path, Version: version.VERSION, FunctionNames: functionNames, } - - // specify output file path - if output == "" { - dir, _ := os.Getwd() - output = filepath.Join(dir, PluginPySourceGenFile) - } else if builtin.IsFolderPathExists(output) { - output = filepath.Join(output, PluginPySourceGenFile) - } - - // generate .debugtalk_gen.py - err = generate(templateContent, pyTemplate, output) - if err != nil { - return err - } - - log.Info().Str("output", output).Str("plugin", path).Msg("build python plugin successfully") - return nil + return templateContent.generatePy(output) } func BuildPlugin(path string, output string) (err error) { diff --git a/hrp/build_test.go b/hrp/build_test.go index c527b4ee..c8932301 100644 --- a/hrp/build_test.go +++ b/hrp/build_test.go @@ -42,3 +42,87 @@ func TestRun(t *testing.T) { t.Fatal() } } + +func TestFindAllPythonFunctionNames(t *testing.T) { + content := ` +def test_1(): # exported function + pass + +def _test_2(): # exported function + pass + +def __test_3(): # private function + pass + +# def test_4(): # commented out function +# pass + +def Test5(): # exported function + pass +` + names := regexPyFunctionName.findAllFunctionNames(content) + if !assert.Contains(t, names, "test_1") { + t.FailNow() + } + if !assert.Contains(t, names, "Test5") { + t.FailNow() + } + if !assert.Contains(t, names, "_test_2") { + t.FailNow() + } + if !assert.NotContains(t, names, "__test_3") { + t.FailNow() + } + // commented out function + if !assert.NotContains(t, names, "test_4") { + t.FailNow() + } +} + +func TestFindAllGoFunctionNames(t *testing.T) { + content := ` +func Test1() { // exported function + return +} + +func testFunc2() { // exported function + return +} + +func main() { // private function + return +} + +func init() { // private function + return +} + +func _Test3() { // exported function + return +} + +// func Test4() { // commented out function +// return +// } +` + names := regexGoFunctionName.findAllFunctionNames(content) + if !assert.Contains(t, names, "Test1") { + t.FailNow() + } + if !assert.Contains(t, names, "testFunc2") { + t.FailNow() + } + if !assert.NotContains(t, names, "main") { + t.FailNow() + } + if !assert.NotContains(t, names, "init") { + t.FailNow() + } + if !assert.Contains(t, names, "_Test3") { + t.FailNow() + } + // commented out function + if !assert.NotContains(t, names, "Test4") { + t.FailNow() + } +} diff --git a/hrp/internal/scaffold/templates/plugin/debugtalk.py b/hrp/internal/scaffold/templates/plugin/debugtalk.py index ea48ff48..334a46c7 100644 --- a/hrp/internal/scaffold/templates/plugin/debugtalk.py +++ b/hrp/internal/scaffold/templates/plugin/debugtalk.py @@ -3,6 +3,11 @@ import time from typing import List +# commented out function will be filtered +# def get_headers(): +# return {"User-Agent": "hrp"} + + def get_user_agent(): return "hrp/funppy"