工具:
在线编译工具
自动检测类并输出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' {base.__name__} <|-down- {cls.__name__}: Inheritance' 12 s+=f' class {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+=' -' 19 else: 20 s+=' +' 21 if(k.__class__.__name__ not in ['function','method']): 22 s+=f'{k.__class__.__name__} {name}' 23 else: 24 s+=f'{name}()' 25 s+=' }' 26 s+=' @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' {base.__name__} <|-down- {cls.__name__}: Inheritance' 415 s+=f' class {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+=' -' 422 else: 423 s+=' +' 424 if(k.__class__.__name__ not in ['function','method']): 425 s+=f'{k.__class__.__name__} {name}' 426 else: 427 s+=f'{name}()' 428 s+=' }' 429 s+=' @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
输出