python_uml_class 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,352 @@
1
+ require "tempfile"
2
+ require "facter"
3
+
4
+ CStruct = Struct.new(:type,
5
+ :file_name,
6
+ :name,
7
+ :block_count,
8
+ :var_list,
9
+ :method_list,
10
+ :inherit_list,
11
+ :composition_list)
12
+
13
+ def get_formatter_path
14
+ return @config["formatter_path"] if @config["formatter_path"].to_s != ""
15
+
16
+ ""
17
+ end
18
+
19
+ def print_uml(out, out_list)
20
+ out_list.each do |o_list|
21
+ if o_list.type == :class_start
22
+ # nop
23
+ elsif o_list.type == :module_start
24
+ out.push "namespace \"#{o_list.name}\" {"
25
+ elsif o_list.type == :class_end
26
+ pp o_list if o_list.name == ""
27
+ out.push "class \"#{o_list.name}\" {"
28
+ # インスタンス変数の出力
29
+ o_list.var_list.uniq.each do |iv|
30
+ out.push iv
31
+ end
32
+ # メソッドの出力
33
+ o_list.method_list.each do |ml|
34
+ out.push ml
35
+ end
36
+ out.push "}"
37
+ # 継承リストの出力
38
+ o_list.inherit_list.each do |ih|
39
+ out.push "\"#{o_list.name}\" -[##{@config["inherit_color"]}]-|> \"#{ih}\""
40
+ end
41
+ # compo
42
+ o_list.composition_list.uniq.each do |co|
43
+ out.push "\"#{o_list.name}\" *-[##{@config["composition_color"]}]- \"#{co}\""
44
+ end
45
+ elsif o_list.type == :module_end
46
+ # インスタンス変数がある場合はモジュール名と同じクラスを定義
47
+ if o_list.var_list.size != 0 or
48
+ o_list.method_list.size != 0 or
49
+ o_list.inherit_list.size != 0 or
50
+ o_list.composition_list.size != 0
51
+ pp o_list if o_list.name == ""
52
+ out.push "class #{o_list.name} {"
53
+ # インスタンス変数の出力
54
+ o_list.var_list.uniq.each do |iv|
55
+ out.push iv
56
+ end
57
+ # メソッドの出力
58
+ o_list.method_list.each do |ml|
59
+ out.push ml
60
+ end
61
+ out.push "}"
62
+ # 継承リストの出力
63
+ o_list.inherit_list.each do |ih|
64
+ out.push "\"#{o_list.name}\" -[##{@config["inherit_color"]}]-|> \"#{ih}\""
65
+ end
66
+ # compo
67
+ o_list.composition_list.uniq.each do |co|
68
+ out.push "\"#{o_list.name}\" *-[##{@config["composition_color"]}]- \"#{co}\""
69
+ end
70
+ end
71
+ out.push "}"
72
+ else
73
+ # error
74
+ puts "error!"
75
+ end
76
+ end
77
+ out
78
+ end
79
+
80
+ def create_uml_class(in_dir, _out_file)
81
+ out = []
82
+ out.push "@startuml"
83
+
84
+ puts "in_dir = #{in_dir}"
85
+ global_var = []
86
+ out_list = []
87
+ def_list = []
88
+ import_list = []
89
+
90
+ Dir.glob("#{in_dir}/**/*.py") do |f|
91
+ import_list.push File.basename(f).split(".")[0]
92
+ end
93
+
94
+ Dir.glob("#{in_dir}/**/*.py") do |f|
95
+ puts f
96
+ puts @config["exclude_path"]
97
+ if @config["exclude_path"] != ""
98
+ if f =~ Regexp.new(@config["exclude_path"])
99
+ puts "skip #{f}"
100
+ next
101
+ end
102
+ end
103
+ buf = ""
104
+ file_name = File.basename(f).split(".")[0]
105
+ Tempfile.create("pylint") do |tmp_file|
106
+ # FileUtils.cp(f, tmp_file.path)
107
+ kernel = Facter.value(:kernel)
108
+ if kernel == "windows"
109
+ open("|#{get_formatter_path} #{f} > #{tmp_file.path}") do |ff|
110
+ if ff.read.to_s != ""
111
+ puts "pylint error #{ff}"
112
+ return
113
+ else
114
+ buf = File.binread tmp_file.path
115
+ end
116
+ end
117
+ else
118
+ open("|#{get_formatter_path} #{f} > #{tmp_file.path}") do |ff|
119
+ puts "|#{get_formatter_path} #{f} > #{tmp_file.path}"
120
+ if ff.read.to_s != ""
121
+ puts "pylint error #{ff}"
122
+ return
123
+ else
124
+ buf = File.binread tmp_file.path
125
+ end
126
+ end
127
+ end
128
+ end
129
+
130
+ cstruct_list = []
131
+ block_count = 0
132
+ method_type = :public
133
+ class_name = ""
134
+ file_struct_list = []
135
+ file_struct_list.push CStruct.new(:class_start, file_name, file_name + ".global", block_count, [], [], [], [])
136
+ file_struct_list.push CStruct.new(:class_end, file_name, file_name + ".global", block_count, [], [], [], [])
137
+ is_def = false
138
+
139
+ # ソースを解析
140
+ buf.each_line do |line|
141
+ next if line =~ /^[\r\n]*$/ # 空行は対象外
142
+
143
+ line.chomp!
144
+ # ブロックの開始/終了
145
+ indent_num = line.match(/^ +/).to_s.size / 4
146
+ puts "block_count=#{indent_num} cstruct_size=#{cstruct_list.size} is_def=#{is_def} #{line}"
147
+ if block_count == indent_num
148
+ # 変化なし
149
+ elsif block_count > indent_num
150
+ # ブロックの終了
151
+ block_count = indent_num
152
+ # 関数の終了
153
+ if is_def == true and def_list[-1].block_count >= block_count
154
+ is_def = false
155
+ method_type = :public
156
+ end
157
+ # クラスの終了
158
+ if cstruct_list.size != 0 && cstruct_list[-1].block_count >= block_count # block_countが一致
159
+ puts "end of #{cstruct_list[-1].name}"
160
+ out_list.push cstruct_list[-1]
161
+ cstruct_list.slice!(-1) # 最後の要素を削除
162
+ method_type = :public
163
+ end
164
+ else
165
+ # ブロックの開始
166
+ block_count = indent_num
167
+ end
168
+ #puts "block_count=#{indent_num} class_count=#{cstruct_list.size} def_count=#{def_list.size} #{line}"
169
+
170
+ # method_type
171
+ if line =~ /@staticmethod/
172
+ method_type = :private
173
+ end
174
+
175
+ # import
176
+ line.match(/import \S+/) do |m|
177
+ import_name = m.to_s.gsub(/import /, "")
178
+ if 0 != import_list.select { |im| im == import_name }.size and file_name != import_name
179
+ if cstruct_list.size != 0
180
+ cstruct_list[-1].composition_list.push import_name
181
+ else
182
+ file_struct_list[-1].composition_list.push import_name
183
+ end
184
+ end
185
+ end
186
+
187
+ # クラスの開始
188
+ if line =~ /^\s*class.*:/
189
+ work = line.gsub(/class\s/, "")
190
+ class_name = work.split("\(")[0].to_s.gsub(/:/, "")
191
+ base_name = work.match(/\(.*\)/).to_s.gsub(/[()]/, "")
192
+ class_name = "#{file_name}.#{class_name}"
193
+ puts "class_name=#{class_name}"
194
+ puts "base_name=#{base_name}"
195
+ out_list.push CStruct.new(:class_start, file_name, class_name, block_count, [], [], [], [])
196
+ cstruct_list.push CStruct.new(:class_end, file_name, class_name, block_count, [], [], [], [])
197
+ # pp line if class_name == ""
198
+ if base_name != ""
199
+ if base_name =~ /,/
200
+ base_name.split(",").each do |name|
201
+ cstruct_list[-1].inherit_list.push name
202
+ end
203
+ else
204
+ cstruct_list[-1].inherit_list.push base_name
205
+ end
206
+ end
207
+ next
208
+ end
209
+
210
+ if line =~ /^\s*private$/
211
+ method_type = :private
212
+ elsif line =~ /^\s*protected$/
213
+ method_type = :protected
214
+ elsif line =~ /^\s*public$/
215
+ method_type = :public
216
+ end
217
+
218
+ if line =~ /^\s*def\s/
219
+ # 関数名を取り出す
220
+ method = line.chomp.gsub(/\s*def\s*/, "")
221
+ unless method =~ /\(/
222
+ # 関数名にカッコをつける
223
+ sp = method.split(" ")
224
+ method = if sp.size > 1
225
+ sp[0].to_s + "(" + sp[1..-1].to_s + ")"
226
+ else
227
+ method + "()"
228
+ end
229
+ end
230
+ if cstruct_list.size != 0
231
+ method_list = cstruct_list[-1].method_list
232
+ case method_type
233
+ when :public
234
+ method_list.push "+ #{method}"
235
+ when :private
236
+ method_list.push "- #{method}"
237
+ when :protected
238
+ method_list.push "# #{method}"
239
+ end
240
+ else
241
+ method_list = file_struct_list[-1].method_list
242
+ method_list.push "+ #{method}"
243
+ end
244
+ def_list.push CStruct.new(:method_start, file_name, method, block_count, [], [], [], [])
245
+ is_def = true
246
+ end
247
+
248
+ # composition_list
249
+ # クラスの呼び出し箇所
250
+ line.match(/\s([A-Z][a-zA-Z]+)\.[a-z]/) do |m|
251
+ c_name = m.to_s.split(".")[0].gsub(/ /, "")
252
+ puts "compo c_name=#{c_name}"
253
+ if cstruct_list.size != 0
254
+ cstruct_list[-1].composition_list.push c_name
255
+ else
256
+ file_struct_list[-1].composition_list.push c_name
257
+ end
258
+ end
259
+
260
+ # クラスの初期化箇所
261
+ line.match(/[\s\.][A-Z][A-Za-z]+\(/) do |m|
262
+ c_name = m.to_s.gsub(/\(/, "").gsub(/[\s\.]/, "")
263
+ puts "compo c_name=#{c_name}"
264
+ if cstruct_list.size != 0
265
+ cstruct_list[-1].composition_list.push c_name
266
+ else
267
+ file_struct_list[-1].composition_list.push c_name
268
+ end
269
+ end
270
+
271
+ # インスタンス変数
272
+ if line =~ /^\s*self\.[a-zA-Z0-9_]+\s+=/ and cstruct_list.size != 0
273
+ line.match(/self\.[a-zA-Z0-9_]+/) do |m|
274
+ instance_var = cstruct_list[-1].var_list
275
+ val = m.to_s.gsub(/self\./, "")
276
+ case method_type
277
+ when :public
278
+ instance_var.push "+ #{val}"
279
+ when :private
280
+ instance_var.push "- #{val}"
281
+ when :protected
282
+ instance_var.push "# #{val}"
283
+ end
284
+ end
285
+ end
286
+
287
+ # クラス変数
288
+ if line =~ /^\s*[a-zA-Z0-9_]+\s+=/ and cstruct_list.size != 0 and is_def == false
289
+ line.match(/[a-zA-Z0-9_]+/) do |m|
290
+ instance_var = cstruct_list[-1].var_list
291
+ val = m.to_s
292
+ instance_var.push "- #{val}"
293
+ end
294
+ end
295
+
296
+ # 外部変数
297
+ if line =~ /^\s*[a-zA-Z0-9_]+\s+=/ and cstruct_list.size == 0 and is_def == false
298
+ line.match(/\$*[a-zA-Z0-9_]+/) do |m|
299
+ file_struct_list[-1].var_list.push "+ #{m}"
300
+ end
301
+ end
302
+ end
303
+
304
+ # ファイルの終了
305
+ if block_count != 0
306
+ puts "endf of #{f}"
307
+ # クラスの終了
308
+ if cstruct_list.size != 0
309
+ puts "end of #{cstruct_list[-1].name}"
310
+ out_list.push cstruct_list[-1]
311
+ cstruct_list.slice!(-1) # 最後の要素を削除
312
+ end
313
+ file_struct_list.each do |fs|
314
+ out_list.push fs
315
+ end
316
+ end
317
+ end
318
+
319
+ # 継承リストとコンポジションリストのチュエック
320
+ out_list.each_index do |i|
321
+ out_list[i].composition_list.each_index do |j|
322
+ # compo_nameがfile_name.class_nameに変更可能かチェック
323
+ compo_name = out_list[i].composition_list[j]
324
+ out_list.select { |a| a.name.split(".")[-1] == compo_name }.each do |m|
325
+ puts "m=#{m.name}"
326
+ out_list[i].composition_list[j] = m.name
327
+ end
328
+ def_list.each do |def_struct|
329
+ # comp_nameが自分で定義した関数名の場合は削除
330
+ #puts "#{compo_name} == #{def_struct.name.split("(")[0]}"
331
+ if compo_name == def_struct.name.split("(")[0]
332
+ puts "match_def #{compo_name} == #{def_struct.name.split("(")[0]}"
333
+ out_list[i].composition_list.delete_at(j)
334
+ end
335
+ end
336
+ end
337
+ out_list[i].inherit_list.each_index do |j|
338
+ # compo_nameがfile_name.class_nameに変更可能かチェック
339
+ inherit_name = out_list[i].inherit_list[j]
340
+ out_list.select { |a| a.name.split(".")[-1] == inherit_name }.each do |m|
341
+ puts "m=#{m.name}"
342
+ out_list[i].inherit_list[j] = m.name
343
+ end
344
+ end
345
+ end
346
+
347
+ # UMLの出力
348
+ out = print_uml(out, out_list)
349
+
350
+ out.push "@enduml"
351
+ out.join("\n")
352
+ end
data/lib/css/index.css ADDED
@@ -0,0 +1,178 @@
1
+ body {
2
+ color: #000000;
3
+ background-color: #cac3ec4f;
4
+ overflow: hidden;
5
+ font-size: 12px;
6
+ }
7
+
8
+ hr {
9
+ color: #ffffff;
10
+ background-color: #000000;
11
+ height: 1px;
12
+ /* 線の太さ */
13
+ border: 1px;
14
+ /* 枠の太さ */
15
+ border-style: solid;
16
+ /* 枠の種類 */
17
+ }
18
+
19
+ .error {
20
+ color: red;
21
+ }
22
+
23
+ .outarea {
24
+ background-color: #FFFFFF;
25
+ margin: 5px;
26
+ padding: 5px;
27
+ width: 95vw;
28
+ height: 50vh;
29
+ overflow: auto;
30
+ }
31
+
32
+ .inarea {
33
+ border: thin solid #000000;
34
+ margin: 5px;
35
+ padding: 5px;
36
+ width: 98%;
37
+ }
38
+
39
+ input.long {
40
+ width: 98%;
41
+ background-color: #FAFAFA;
42
+ margin: 5px;
43
+ padding: 5px;
44
+ }
45
+
46
+ textarea.long {
47
+ width: 95vw;
48
+ height: 50vh;
49
+ }
50
+
51
+ .ui-widget {
52
+ font-size: 12px;
53
+ }
54
+
55
+ .ui-autocomplete {
56
+ max-height: 45vh;
57
+ max-width: 90wh;
58
+ overflow-y: auto;
59
+ overflow-x: auto;
60
+ padding-right: 10px;
61
+ }
62
+
63
+ #jquery-ui-autocomplete label {
64
+ float: left;
65
+ margin-right: 0.5em;
66
+ color: black;
67
+ }
68
+
69
+ .ui-autocomplete.ui-front {
70
+ max-height: 250px;
71
+ overflow-y: auto;
72
+ /* prevent horizontal scrollbar */
73
+ overflow-x: hidden;
74
+ /* add padding to account for vertical scrollbar */
75
+ z-index: 1000 !important;
76
+ }
77
+
78
+ .ui-dialog {
79
+ position: absolute;
80
+ top: 0;
81
+ left: 0;
82
+ padding: .2em;
83
+ outline: 0;
84
+ }
85
+
86
+ .long {
87
+ width: 90%;
88
+ }
89
+
90
+ #setting_dialog {
91
+ color: #ffffff;
92
+ background-color: #000000;
93
+ }
94
+
95
+ .setting_name {
96
+ width: 200px;
97
+ color: #ffffff;
98
+ background-color: #000000;
99
+ }
100
+
101
+ .setting_value {
102
+ width: 300px;
103
+ color: #ffffff;
104
+ background-color: #000000;
105
+ }
106
+
107
+ .setting_checkbox {
108
+ color: #ffffff;
109
+ background-color: #000000;
110
+ }
111
+
112
+ ul.log {
113
+ list-style-type: none;
114
+ font-size: 12px;
115
+ color: #000000;
116
+ }
117
+
118
+ input[type="search"] {
119
+ -webkit-appearance: searchfield;
120
+ }
121
+
122
+ input[type="search"]::-webkit-search-cancel-button {
123
+ -webkit-appearance: searchfield-cancel-button;
124
+ }
125
+
126
+ /* menu */
127
+ .menu {
128
+ display: flex;
129
+ justify-content: flex-start;
130
+ list-style-type: none;
131
+ color: #393737;
132
+ padding: 0;
133
+ font-size: 12px;
134
+ font-weight: bold;
135
+ }
136
+
137
+ .menu li {
138
+ position: relative;
139
+ width: 100px;
140
+ margin-left: 1px;
141
+ padding: 5px;
142
+ background: #d8dada;
143
+ list-style-type: none;
144
+ }
145
+
146
+ .menu li a {
147
+ color: rgb(20, 114, 192);
148
+ }
149
+
150
+ .menu li a:hover {
151
+ background: #eba399;
152
+ }
153
+
154
+ .menuSub {
155
+ position: absolute;
156
+ margin-left: -6px;
157
+ padding: 0;
158
+ display: none;
159
+ }
160
+
161
+ /*.openが付与された時、表示の設定*/
162
+ .menuSub.open {
163
+ display: block;
164
+ }
165
+
166
+ .menuSub li a {
167
+ padding: 5px;
168
+ margin-left: -5px;
169
+ margin-right: -5px;
170
+ margin-bottom: -5px;
171
+ display: block;
172
+ color: rgb(20, 114, 192);
173
+ text-decoration: none;
174
+ }
175
+
176
+ .menuSub li a:hover {
177
+ background: #eba399;
178
+ }
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import sys
4
+ import ast
5
+ import astor
6
+ import pprint
7
+
8
+ def remove_comments_from_code(code):
9
+ # コードをASTに変換
10
+ tree = ast.parse(code)
11
+
12
+ #print("----------")
13
+ #print(ast.dump(tree, indent=4))
14
+ #print("----------")
15
+
16
+ # ASTを操作してコメントを削除
17
+ for node in ast.walk(tree):
18
+
19
+ #print(vars(node))
20
+ #print("--------------------------------------------------")
21
+ #pprint.pprint(vars(node))
22
+ if isinstance(node, ast.Expr) and isinstance(node.value, ast.Str) and isinstance(node.value.s, str):
23
+ # 文字列としてコメントが格納されているExprノードを削除
24
+ if node.value.s.startswith(("#", '"""', "'''")):
25
+ node.value.s = "" # コメントを空文字列に設定
26
+
27
+ # ASTからコードに変換
28
+ modified_code = astor.to_source(tree)
29
+
30
+ return modified_code
31
+
32
+ if __name__ == "__main__":
33
+ file = sys.argv[1]
34
+ f = open(file, "r")
35
+ code_without_comments = remove_comments_from_code(f.read())
36
+ print(code_without_comments)
@@ -0,0 +1,80 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+
4
+ <head>
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
7
+ <title>PythonUMLClass</title>
8
+ <!-- jQuery -->
9
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
10
+ <!-- jQuery UI -->
11
+ <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
12
+ <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
13
+
14
+ <script src="js/main.js"></script>
15
+ <link rel="stylesheet" href="css/index.css" type="text/css">
16
+ </head>
17
+
18
+ <body>
19
+ <div id="msg_dialog" style="display:none;">
20
+ <div id="msg_text">message</div>
21
+ </div>
22
+ <hr>
23
+ <!-- menu -->
24
+ <div id="setting_dialog" style="display:none;">
25
+ <dl id="wrap"></dl>
26
+ </div>
27
+ <div>
28
+ <ul class="menu">
29
+ <Li><a href="#" id="exec" name="exec">実行</a></li>
30
+ <li><a href="#" id="stop" name="stop">停止</a></li>
31
+ <li>
32
+ 設定
33
+ <ul class="menuSub">
34
+ <li><a href="#" id="setting">設定</a></li>
35
+ <li><a href="#" id="save_setting">設定保存</a></li>
36
+ <li><a href="#" id="load_setting">設定読み込み</a></li>
37
+ </ul>
38
+ </li>
39
+ <!--
40
+ <li>
41
+ menu3
42
+ <ul class="menuSub">
43
+ <li><a href="#">menu3-1</a></li>
44
+ <li><a href="#">menu3-2</a></li>
45
+ </ul>
46
+ </li>
47
+ -->
48
+ </ul>
49
+ </div>
50
+
51
+ <table>
52
+ <tr>
53
+ <td>
54
+ <div id="dialog1" style="display:none;">
55
+ <input class="inarea" type="search" name="search_str" id="search_str">
56
+ </div>
57
+ </td>
58
+ <td class="long"><input class="inarea" type="search" id="inDir" name="inDir"></td>
59
+ <td><input type="button" id="select_dir" value="対象フォルダ" /></td>
60
+ </tr>
61
+ <tr>
62
+ <td>
63
+ <div id="dialog2" style="display:none;">
64
+ <input class="inarea" type="search" name="search_str2" id="search_str2">
65
+ </div>
66
+ </td>
67
+ <td class="long"><input class="inarea" type="search" id="outFile" name="outFile"></td>
68
+ <td><input type="button" id="select_file" value="出力ファイル" /></td>
69
+ <td><input type="button" id="open_file" value="開く" /></td>
70
+ </tr>
71
+ </table>
72
+
73
+ <div class="outarea">
74
+ <ul name="log" id="log" class="log">
75
+ </ul>
76
+ </div>
77
+
78
+ </body>
79
+
80
+ </html>