工具:
在线编译工具
自动检测类并输出uml文件脚本

1 def genUML(cs,outfile="d://test.uml"):
2
3 ignoredNames=['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__','__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__','__dict__','__module__','__weakref__']
4
5 s=''
6 s+='@startuml'
7 print(__file__.split('\\'))
8 for c in cs:
9 cls=eval(c)
10 for base in cls.__bases__:
11 s+=f'\n{base.__name__} <|-down- {cls.__name__}: Inheritance'
12 s+=f'\nclass {cls.__name__} {{'
13 for name in list(c for c in dir(cls)):
14 if(name in ignoredNames):
15 continue
16 k=getattr(cls,name)
17 if(name.startswith('_')):
18 s+='\n -'
19 else:
20 s+='\n +'
21 if(k.__class__.__name__ not in ['function','method']):
22 s+=f'{k.__class__.__name__} {name}'
23 else:
24 s+=f'{name}()'
25 s+='\n}'
26 s+='\n@enduml'
27 print(s)
28 with open(outfile,'w') as f:
29 f.write(s)
30 return s
31 pass
32
33 #获取当前可视的所有类,简单过滤
34 cs=list(c for c in dir() if(not c.startswith('_') and c[0].isupper()))
35
36 #生成
37 genUML(cs)
38
39 #copy uml代码到网站并生成
实例

1 import threading
2 import socket
3 import time
4 import json
5 threadLock = threading.Lock()
6
7 class D_Print(object):
8 def __call__(self,func):
9 def _call(*args,**kw):
10 output=None
11 error=None
12
13 print(func.__name__)
14 try:
15 output=func(*args,**kw) # 被装饰的函数
16 except Exception as e:
17 error=e
18
19 self.saveAs(args=args,
20 kw=kw,
21 output=output,
22 error=error)
23
24 if(error):
25 raise error
26 else:
27 return output
28
29 return _call
30
31 def saveAs(self,*,args,kw,output,error): # 演示
32 # print(f'Debug get args,kw={args},{kw}')
33 # print(f'Debug get output={output}')
34 # print(f'Debug get error={error}')
35 pass
36
37
38 class ThreadProxy():
39 @staticmethod
40 def create(target=None,args=[],name=''):
41 if(target):
42 thread = threading.Thread(target=target,args=args)
43 thread.setDaemon(True)
44 thread.setName(name)
45 thread.start()
46 return thread
47 else:
48 return None
49 pass
50 @staticmethod
51 def printThreads():
52 threadLock.acquire()
53 count=0
54 print('-----Printing----')
55 for t in threading.enumerate():
56 count+=1
57 print(f'|{count:02d}:Thread={t.name}')
58 print('--------End------')
59 threadLock.release()
60 pass
61 pass
62
63 class CXTMsg():
64 SPEC=b'CXTMSG'
65 m_A={'key':'A','action':'do A'}
66 m_B={'key':'B','action':'do B'}
67 m_M={'key':'m','action':'do m'}
68 pass
69 class CXTMsgBase():
70 SPEC=b'CXTMSG'
71 @classmethod
72 def readMessage(cls,msgBytes):
73 mb=msgBytes
74 msg=None
75 idx=msgBytes.find(cls.SPEC)
76 print('readMessage idx',idx)
77 if(idx<0):
78 print(f'Discard {len(msgBytes)}. {msgBytes}')
79 msgBytes=b''
80 return msg,msgBytes
81 print(msgBytes)
82 try:
83 print('Parsing')
84 msgBytes=msgBytes[idx:]
85 head=msgBytes[:len(CXTMsgBase.SPEC)].decode('utf-8')
86 print('head',head)
87 msgBytes=msgBytes[len(CXTMsgBase.SPEC):]
88 LoL=int(msgBytes[:2].decode('utf-8'),16)
89 print('LoL',LoL)
90 msgBytes=msgBytes[2:]
91 LoV=int(msgBytes[:LoL].decode('utf-8'),16)
92 print('LoV',LoV)
93 msgBytes=msgBytes[LoL:]
94 value=msgBytes[:LoV]
95 print('value',value)
96
97 msg=cls.fromBytes(value)
98
99 msgBytes=msgBytes[LoV:]
100 print(msg)
101 return msg,msgBytes
102 except Exception as e:
103 print('readMessage Parsing Error=',e)
104 msg=None
105 msgBytes=mb
106 return msg,msgBytes
107 pass
108 pass
109 @classmethod
110 def packMessage(cls,msg):
111 head=cls.SPEC
112 value=cls.toBytes(msg)
113 LoV=("%X"%len(value)).encode('utf-8') # 16进制
114 #LoV=("%X"%1024*10).encode('utf-8') #almost no infi
115 LoL=("%02x"%(len(LoV))).encode('utf-8') #16进制
116 # print(LoV)
117 # print(len(LoL))
118 return head+LoL+LoV+value
119 pass
120 @staticmethod
121 def toBytes(msg,encoding='utf-8',errors='ignore'):
122 return json.dumps(msg).encode(encoding='utf-8')
123 pass
124 @staticmethod
125 def fromBytes(msgBytes,encoding='utf-8',error='ignore'):
126 '''msgBytes or msgStr'''
127 msgStr=msgBytes.decode(encoding=encoding,errors=error)
128 return json.loads(msgStr)
129 pass
130 pass
131 class M_Notify(CXTMsgBase):
132 def __init__(self):
133 pass
134 pass
135
136 class ComBase():
137 def __init__():
138 self.name='Base'
139 self._msgStack=[]
140 pass
141 @property
142 def msgStack(self):
143 return self._msgStack
144 @msgStack.setter
145 def msgStack(self,value):
146 threadLock.acquire()
147 print(self.name,'msgStack Update',value)
148 self._msgStack=value
149 threadLock.release()
150 pass
151 def note(self,*args):
152 if(self.name=='server'):
153 print(f'*[{self}]->',('%s'*len(args))%(args),flush=True)
154 else:
155 print(f' [{self}]->',('%s'*len(args))%(args),flush=True)
156 pass
157 def __str__(self):
158 return self.name
159 pass
160 def recvLoop(self,sock):
161 """
162 消息处理
163 """
164 try:
165 recvBuff=b''
166 while True:
167 recvBuff+= sock.recv(1024)
168 if(len(recvBuff)>0):
169 print('recvBuff',recvBuff)
170 recvBuff=self.readMessage(recvBuff)
171 #self.recvLoopBody(sock)
172 except ConnectionResetError as e:
173 self.note(self,type(e).__name__,f'={e}')
174 except Exception as e:
175 self.note(self,type(e).__name__,f'={e}')
176 finally:
177 sock.close()
178 self.onRecvClosed(sock)
179 def readMessage(self,recvBuff):
180 while(True):
181 msg,recvBuff=CXTMsgBase.readMessage(recvBuff)
182 print(msg,recvBuff)
183 if(msg!=None):
184 print('update msgStack')
185 self.msgStack+=[msg]
186 else:
187 break
188 pass
189 return recvBuff
190 pass
191 # def recvLoopBody(self,sock):
192 # bytes = sock.recv(1024)
193 # #name=sock.getpeername()
194 # if(bytes==b''):
195 # raise Exception('Empty Msg Mean Closed!')
196 # self.note(f"消息:", self._bytes2Str(bytes))
197 pass
198 def msgLoop(self):
199 """
200 消息处理
201 """
202 try:
203 while True:
204 self.msgLoopBody()
205 time.sleep(10)
206 except Exception as e:
207 self.note(self,type(e).__name__,f'={e}')
208 finally:
209 print('msgLoop End')
210 pass
211 def msgLoopBody(self):
212 print(self.name,'msgStack',self.msgStack)
213 msg=None
214 threadLock.acquire()
215 if(len(self.msgStack)>0):
216 msg=self.msgStack.pop(0)
217 threadLock.release()
218 if(msg):
219 print(f'Done {msg}')
220 pass
221 def sendMsg(self):
222 pass
223 def onRecvClosed(self,sock):
224 #Default Do Nothing
225 pass
226 # def message2json(self):
227 # pass
228 # def json2message(self):
229 # pass
230 def _bytes2Str(self,b,encoding='utf8',errors='ignore'):
231 return b.decode(encoding=encoding,errors=errors)
232 pass
233 def _str2Bytes(self,s,encoding='utf8',errors='ignore'):
234 if(type(s) is bytes):
235 return s
236 else:
237 return s.encode(encoding=encoding,errors=errors)
238 pass
239 pass
240 class ClientApp(ComBase):
241
242 @D_Print()
243 def __init__(self,name='client',address = ('127.0.0.1', 8712)):
244 self.name=name
245 self.raddress=address
246 self.serverSocket=None
247 self.msgStack=[]
248 pass
249 def start(self):
250 self.serverSocket = socket.socket() # 创建 socket 对象
251 self.serverSocket.connect(self.raddress)
252 #self.serverSocket.send("连接了".encode('utf8'))
253
254 ThreadProxy.create(target=self.recvLoop
255 ,args=[self.serverSocket]
256 ,name='Client recvThread')
257
258 ThreadProxy.create(target=self.msgLoop
259 ,args=[]
260 ,name='Client msgThread')
261 return self
262 pass
263 def send(self,cmd):
264 self.serverSocket.send(self._str2Bytes(cmd))
265 pass
266 def sendMsg(self,msg):
267 msgBytes=CXTMsgBase.packMessage(msg)
268 self.serverSocket.send(msgBytes)
269 def close(self,sock=None):
270 if(not sock):
271 sock=self.serverSocket
272
273 #threadLock.acquire()
274 sock.close()
275 del sock
276 #threadLock.release()
277 pass
278 pass
279
280 class ServerApp(ComBase):
281 @D_Print()
282 def __init__(self,name='server',address = ('127.0.0.1', 8712)):
283 self.name=name
284 self.address=address
285 self.serverSocket=None
286 self.handlerGroup=[]
287 self.msgStack=[]
288 pass
289 def start(self):
290 #startListener
291 self.serverSocket = socket.socket(
292 socket.AF_INET,
293 socket.SOCK_STREAM) # 创建 socket 对象
294 self.serverSocket.bind(self.address)
295 self.serverSocket.listen(5) # 最大等待数(有很多人理解为最大连接数,其实是错误的)
296
297 ThreadProxy.create(target=self.acceptLoop
298 ,args=[]
299 ,name='Server acceptThread')
300
301 ThreadProxy.create(target=self.msgLoop
302 ,args=[]
303 ,name='Server msgThread')
304
305 return self
306 pass
307 def acceptLoop(self):
308 """
309 接收新连接
310 """
311 while True:
312 self.note(f"等待客户端连接...")
313 clientSocket, _ = self.serverSocket.accept() # 阻塞,等待客户端连接
314 self.onClientAccepted(clientSocket)
315 pass
316 pass
317 def onClientAccepted(self,clientSocket):
318 # # 给每个客户端创建一个独立的线程进行管理
319 # thread = threading.Thread(target=cls.recvLoop, args=(clientSocket,))
320 # # 设置成守护线程
321 # thread.setDaemon(True)
322 # thread.start()
323 self.registClient(clientSocket)
324
325 ThreadProxy.create(target=self.recvLoop
326 ,args=[clientSocket]
327 ,name='Server RecvThread')
328 pass
329 def registClient(self,clientSocket):
330 self.note(f"Client Registing")
331 threadLock.acquire()
332
333 self.handlerGroup.append(clientSocket)
334
335 name=clientSocket.getpeername()
336 self.note(f"{name} Connected")
337 self.note(f"Now Client Count={len(self.handlerGroup)}")
338
339 threadLock.release()
340 self.note(f"Client Registed")
341 pass
342 def onRecvClosed(self,sock):
343 self.onClientClosed(sock)
344 pass
345 def onClientClosed(self,clientSocket):
346 threadLock.acquire()
347
348 idx=self.handlerGroup.index(clientSocket)
349 self.handlerGroup.remove(clientSocket)
350
351 self.note(f"client {idx} left")
352 self.note(f"Now Client Count={len(self.handlerGroup)}")
353
354 threadLock.release()
355 pass
356 def send(self,clientIdx,cmd):
357 self.note('Sending...')
358 c=self.getClientSocket(clientIdx)
359 if(c):
360 c.send(self._str2Bytes(cmd))
361 else:
362 self.note('send error')
363 pass
364 pass
365 def sendMsg(self,clientIdx,msg):
366 self.note('Sending...')
367 c=self.getClientSocket(clientIdx)
368 if(c):
369 msgBytes=CXTMsgBase.packMessage(msg)
370 c.send(msgBytes)
371 else:
372 self.note('send error')
373 pass
374 def getClientSocket(self,idx):
375 try:
376 return self.handlerGroup[idx]
377 except:
378 print('getClientSocket error')
379 return None
380 pass
381 def close(self,clientIdx):
382 self.note('Client Closeing...')
383 c=self.getClientSocket(clientIdx)
384 if(c):
385 threadLock.acquire()
386 c.close()
387 threadLock.release()
388 else:
389 self.note('close error')
390 pass
391 pass
392 def broadcast(self,s):
393 for sock in self.handlerGroup:
394 threadLock.acquire()
395 try:
396 sock.send(self._str2Bytes(s))
397 except:
398 self.note('BC Error.')
399 pass
400 threadLock.release()
401 pass
402 pass
403
404 def genUML(cs,outfile="d://test.uml"):
405
406 ignoredNames=['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__','__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__','__dict__','__module__','__weakref__']
407
408 s=''
409 s+='@startuml'
410 print(__file__.split('\\'))
411 for c in cs:
412 cls=eval(c)
413 for base in cls.__bases__:
414 s+=f'\n{base.__name__} <|-down- {cls.__name__}: Inheritance'
415 s+=f'\nclass {cls.__name__} {{'
416 for name in list(c for c in dir(cls)):
417 if(name in ignoredNames):
418 continue
419 k=getattr(cls,name)
420 if(name.startswith('_')):
421 s+='\n -'
422 else:
423 s+='\n +'
424 if(k.__class__.__name__ not in ['function','method']):
425 s+=f'{k.__class__.__name__} {name}'
426 else:
427 s+=f'{name}()'
428 s+='\n}'
429 s+='\n@enduml'
430 print(s)
431 with open(outfile,'w') as f:
432 f.write(s)
433 return s
434 pass
435
436 #获取当前可视的所有类,简单过滤
437 cs=list(c for c in dir() if(not c.startswith('_') and c[0].isupper()))
438
439 #生成
440 genUML(cs)
441
442 #copy uml代码到网站并生成
输出

@startuml
object <|-down- CXTMsg: Inheritance
class CXTMsg {
+bytes SPEC
+dict m_A
+dict m_B
+dict m_M
}
object <|-down- CXTMsgBase: Inheritance
class CXTMsgBase {
+bytes SPEC
+fromBytes()
+packMessage()
+readMessage()
+toBytes()
}
ComBase <|-down- ClientApp: Inheritance
class ClientApp {
-_bytes2Str()
-_str2Bytes()
+close()
+msgLoop()
+msgLoopBody()
+property msgStack
+note()
+onRecvClosed()
+readMessage()
+recvLoop()
+send()
+sendMsg()
+start()
}
object <|-down- ComBase: Inheritance
class ComBase {
-_bytes2Str()
-_str2Bytes()
+msgLoop()
+msgLoopBody()
+property msgStack
+note()
+onRecvClosed()
+readMessage()
+recvLoop()
+sendMsg()
}
object <|-down- D_Print: Inheritance
class D_Print {
-__call__()
+saveAs()
}
CXTMsgBase <|-down- M_Notify: Inheritance
class M_Notify {
+bytes SPEC
+fromBytes()
+packMessage()
+readMessage()
+toBytes()
}
ComBase <|-down- ServerApp: Inheritance
class ServerApp {
-_bytes2Str()
-_str2Bytes()
+acceptLoop()
+broadcast()
+close()
+getClientSocket()
+msgLoop()
+msgLoopBody()
+property msgStack
+note()
+onClientAccepted()
+onClientClosed()
+onRecvClosed()
+readMessage()
+recvLoop()
+registClient()
+send()
+sendMsg()
+start()
}
object <|-down- ThreadProxy: Inheritance
class ThreadProxy {
+create()
+printThreads()
}
@enduml
输出

