[Python learning] automatically generate java code

There is a requirement in the project that a base class has a comparison method and a Copy method. In some places, these two methods need to be called frequently. For all subclasses, you need to override these two methods and maintain some member variables within the class. For example, there is a variable M_ Imyval, this needs to be maintained in the Copy method m_ iMyVal = data. m_ iMyVal; In the IsEqual method, maintain if (this. m_iMyVal! = data. m_iMyVal) return false wait. Once there are more variables in a subclass, for example, there are more than ten or more such variables in a class, and they may be deleted or added in the future, it will be a little painful to maintain, because the corresponding methods need to be supplemented in both places, and each of them can not be omitted or written wrong.

These subclasses are generally data classes. When writing code, you often write a considerable number of variables at one time. If you write one and then write the corresponding code in two places, the error rate may be low, but it is easy to interrupt your thinking; However, if you supplement the code after writing all variables, there will be a suspicion of missing and wrong writing. Once missing, there may be no bug s at that time, and it is very painful to check the errors later, resulting in the decline of development efficiency. Another trouble is that the comparison method and copy method may be different for different types of variables (for example, floating-point numbers need eps, list s need cyclic comparison, etc.).

So did you write a variable and tell the system that this variable needs to be supplemented in the comparison and copy methods, and then "someone" will help me supplement it automatically? Because the author uses C# language development, he naturally thought of the method of adding labels. Then he can find all member variables with such labels through reflection and operate directly in the comparison and copy methods.

However, I'm worried about the low performance of reflection, so I don't think about it for the time being. Instead, I use the following method of automatically generating code using Python.

Use Python to read the file, regularly query the variables with some labels or comments, collect these variables, and then replace the comparison and copy methods of the original file.

Then the workflow becomes:

  1. Write required member variables

  2. If you need to automatically generate code for it, add the corresponding tag (the annotation method is not adopted because the tag can be automatically completed to prevent regular error finding).

  3. Execute the batch file and automatically generate code for the required file (if there is this part of code before, it is replaced).

The whole process hardly takes any time, but the automatic code generation greatly improves the development efficiency.

Python source code is as follows:

 1 # -*- encoding:utf-8 -*-
 2 
 3 import os
 4 import sys
 5 import re
 6 
 7 
 8 def process_file(path):
 9     keyword = 'debug_util.'
10     modify_flag = False
11 
12     auto_start = "// python automatic generation start"
13     auto_end = "// python automatic generation end"
14     normalLabel = "\[NormalLabel\]"
15     floatLabel = "\[FloatLabel\]"
16     vec3Lable = "\[Vec3Label\]"
17 
18     normalCmp = "[NormalLabel]"
19     floatCmp = "[FloatLabel]"
20     vec3Cmp = "[Vec3Label]"
21 
22     with open(path, 'rb') as f:
23         txt = f.read()
24         # print ("txt is " + txt)
25         pattern = "(%s|%s|%s)[\s\S]+?public\s+\w+\s+(\w+)" % (normalLabel, floatLabel, vec3Lable)
26         matchStr = re.findall(pattern, txt)
27         # print (matchStr)
28         labelList = []
29         varList = []
30         for i, packed in enumerate(matchStr):
31             labelName = packed[0]
32             varName = packed[1]
33             labelList.append(labelName)
34             varList.append(varName)
35             # print (labelName + ".." + varName)
36 
37         # Start finding the name of the class
38         pattern = "public class (\w+)\s*:\s*SkillBaseData"
39         matchStr = re.findall(pattern, txt)
40         className = ""
41         for i, packed in enumerate(matchStr):
42             className = packed
43             break # There will only be one class name
44 
45 
46         ## Find comment section
47         index = 0
48         begin = txt.find(auto_start, index) + len(auto_start)
49         end = txt.find(auto_end, index)
50         copyContent = ""
51         equalContent = ""
52         for labelName, varName in zip(labelList, varList):
53             copyContent += "\t\t\tthis." + varName + " = data." + varName + ";\n"
54             if labelName == normalCmp:
55                 equalContent += ("\t\t\tif(this.%s != data.%s) return 1;\n" % (varName, varName))
56             elif labelName == floatCmp:
57                 equalContent += ("\t\t\tif(FloatNotEqual(this.%s, data.%s)) return 1;\n" % (varName, varName))
58             elif labelName == vec3Cmp:
59                 equalContent += ("\t\t\tif(Vec3NotEqual(this.%s, data.%s)) return 1;\n" % (varName, varName))
60 
61 
62         auto_code = ""
63         auto_code += (("\n\t\tpublic override void CopyDataFrom(SkillBaseData baseData)\n\t\t{\n\t\t\tvar data = (%s)baseData;\n" % className) + copyContent + "\t\t}\n")
64         auto_code += (("\n\t\tpublic override int IsEqual(SkillBaseData baseData)\n\t\t{\n\t\t\tvar data = (%s)baseData;\n" % className) + equalContent + "\t\t\treturn 0;\n\t\t}\n")
65         print (txt[:begin] + auto_code + txt[end:])
66         new_txt = txt[:begin] + auto_code + "\t\t" + txt[end:]
67         with open(path, 'wb') as f:
68             f.write(new_txt)
69 
70 def okPath(path):
71     if path.endswith("TestSkillData.cs"):
72         return True
73 
74     return False
75 
76 if __name__ == '__main__':
77     cwd = os.getcwd()
78     _len = len(cwd) - 22  # 22 is the length of the last level directory. Here is the magic number. It can be changed according to the needs. Of course, you can also find the required directory by other methods = =.
79     cwd = cwd[:_len] + 'SkillDerivedData'
80     print(cwd)
81     # cwd = cwd + '/Package/Script/Python/'
82     directiory = os.walk(cwd)
83     for root, dirs, files in directiory:
84         for f in files:
85             if okPath(f):
86                 process_file(os.path.join(root, f))
87                 # print("name is " + f)

After writing C# directly run the following bat.

Finally, the effect of the generated code is shown in the figure below. It feels pretty = =. (different processing methods are provided for three different labels):

In general, I learned a new way to deal with problems and gained a lot. Finally, thank @ hamster who taught me this method and @ Cong who provided regular help.

 

 
 

Keywords: Python

Added by narimanam on Tue, 11 Jan 2022 06:09:44 +0200