1
1
import os
2
2
from enum import Enum
3
+ from typing import List , Union
3
4
4
5
import mypy .types as mpt
5
6
import mypy .nodes as mpn
@@ -158,6 +159,9 @@ def __init__(self, overloaded_func_def: mpn.OverloadedFuncDef):
158
159
if isinstance (item , mpn .Decorator ):
159
160
self .definitions .append (FunctionSymbol (item .func , decorators = item .original_decorators ))
160
161
162
+ def __eq__ (self , other ):
163
+ return isinstance (other , OverloadedFunctionSymbol ) and self .to_proto () == other .to_proto ()
164
+
161
165
def to_proto (self ) -> symbols_pb2 .OverloadedFunctionSymbol :
162
166
pb_overloaded_func = symbols_pb2 .OverloadedFunctionSymbol ()
163
167
pb_overloaded_func .name = self .name
@@ -188,6 +192,9 @@ def __init__(self, func_def: mpn.FuncDef, decorators=None):
188
192
if decorator_name is not None :
189
193
self .resolved_decorator_names .append (decorator_name )
190
194
195
+ def __eq__ (self , other ):
196
+ return isinstance (other , FunctionSymbol ) and self .to_proto () == other .to_proto ()
197
+
191
198
def to_proto (self ) -> symbols_pb2 .FunctionSymbol :
192
199
pb_func = symbols_pb2 .FunctionSymbol ()
193
200
pb_func .name = self .name
@@ -246,6 +253,19 @@ def __init__(self, type_info: mpn.TypeInfo):
246
253
self .metaclass_name = class_def .metaclass .fullname
247
254
self .has_decorators = len (class_def .decorators ) > 0
248
255
256
+ def __eq__ (self , other ):
257
+ if not isinstance (other , ClassSymbol ):
258
+ return False
259
+ return (self .name == other .name
260
+ and self .fullname == other .fullname
261
+ and self .super_classes == other .super_classes
262
+ and self .mro == other .mro
263
+ and self .is_enum == other .is_enum
264
+ and self .is_generic == other .is_generic
265
+ and self .is_protocol == other .is_protocol
266
+ and self .metaclass_name == other .metaclass_name
267
+ and self .has_decorators == other .has_decorators )
268
+
249
269
def to_proto (self ) -> symbols_pb2 .ClassSymbol :
250
270
pb_class = symbols_pb2 .ClassSymbol ()
251
271
pb_class .name = self .name
@@ -266,6 +286,86 @@ def to_proto(self) -> symbols_pb2.ClassSymbol:
266
286
return pb_class
267
287
268
288
289
+ class MergedFunctionSymbol :
290
+ def __init__ (self , function_symbol : FunctionSymbol , valid_for : List [str ]):
291
+ self .function_symbol = function_symbol
292
+ self .valid_for = valid_for
293
+
294
+ def to_proto (self ) -> symbols_pb2 .FunctionSymbol :
295
+ pb_func = self .function_symbol .to_proto ()
296
+ for elem in self .valid_for :
297
+ pb_func .valid_for .append (elem )
298
+ return pb_func
299
+
300
+
301
+ class MergedOverloadedFunctionSymbol :
302
+ def __init__ (self , overloaded_function_symbol : OverloadedFunctionSymbol , valid_for : List [str ]):
303
+ self .overloaded_function_symbol = overloaded_function_symbol
304
+ self .valid_for = valid_for
305
+
306
+ def to_proto (self ) -> symbols_pb2 .FunctionSymbol :
307
+ pb_func = self .overloaded_function_symbol .to_proto ()
308
+ for elem in self .valid_for :
309
+ pb_func .valid_for .append (elem )
310
+ return pb_func
311
+
312
+
313
+ class MergedClassSymbol :
314
+ def __init__ (self , reference_class_symbols : ClassSymbol , merged_methods , merged_overloaded_methods ,
315
+ valid_for : List [str ]):
316
+ # nested class symbols functions are not relevant anymore
317
+ self .class_symbol = reference_class_symbols
318
+ self .methods = merged_methods
319
+ self .overloaded_methods = merged_overloaded_methods
320
+ self .valid_for = valid_for
321
+
322
+ def to_proto (self ) -> symbols_pb2 .ClassSymbol :
323
+ pb_class = symbols_pb2 .ClassSymbol ()
324
+ pb_class .name = self .class_symbol .name
325
+ pb_class .fully_qualified_name = self .class_symbol .fullname
326
+ pb_class .super_classes .extend (self .class_symbol .super_classes )
327
+ pb_class .mro .extend (self .class_symbol .mro )
328
+ pb_class .has_decorators = self .class_symbol .has_decorators
329
+ pb_class .has_metaclass = self .class_symbol .has_metaclass
330
+ pb_class .is_enum = self .class_symbol .is_enum
331
+ pb_class .is_generic = self .class_symbol .is_generic
332
+ pb_class .is_protocol = self .class_symbol .is_protocol
333
+ if self .class_symbol .metaclass_name is not None :
334
+ pb_class .metaclass_name = self .class_symbol .metaclass_name
335
+ for method in self .methods :
336
+ for elem in self .methods [method ]:
337
+ pb_class .methods .append (elem .to_proto ())
338
+ for overloaded_func in self .overloaded_methods :
339
+ for elem in self .overloaded_methods [overloaded_func ]:
340
+ pb_class .overloaded_methods .append (elem .to_proto ())
341
+ for elem in self .valid_for :
342
+ pb_class .valid_for .append (elem )
343
+ return pb_class
344
+
345
+
346
+ class MergedModuleSymbol :
347
+ def __init__ (self , fullname , classes , functions , overloaded_functions ):
348
+ self .fullname = fullname
349
+ self .classes = classes
350
+ self .functions = functions
351
+ self .overloaded_functions = overloaded_functions
352
+
353
+ def to_proto (self ):
354
+ pb_module = symbols_pb2 .ModuleSymbol ()
355
+ pb_module .name = self .fullname # FIXME: is it even useful to have name?
356
+ pb_module .fully_qualified_name = self .fullname
357
+ for cls in self .classes :
358
+ for elem in self .classes [cls ]:
359
+ pb_module .classes .append (elem .to_proto ())
360
+ for func in self .functions :
361
+ for elem in self .functions [func ]:
362
+ pb_module .functions .append (elem .to_proto ())
363
+ for overloaded_func in self .overloaded_functions :
364
+ for elem in self .overloaded_functions [overloaded_func ]:
365
+ pb_module .overloaded_functions .append (elem .to_proto ())
366
+ return pb_module
367
+
368
+
269
369
class ModuleSymbol :
270
370
def __init__ (self , mypy_file : mpn .MypyFile ):
271
371
self .name = mypy_file .name
@@ -338,15 +438,25 @@ def extract_return_type(func_def: mpn.FuncDef):
338
438
return TypeDescriptor (func_type .ret_type )
339
439
340
440
341
- def save_module (mypy_file : mpn .MypyFile , save_as_text = True , output_dir_name = "output" ,
342
- save_location = "../../src/main/resources/org/sonar/python/types" ):
343
- ms = ModuleSymbol (mypy_file )
441
+ def save_module (ms : Union [ModuleSymbol , MergedModuleSymbol ], is_debug = False , debug_dir = "output" ):
344
442
ms_pb = ms .to_proto ()
345
- save_dir = f" { save_location } / { output_dir_name } "
346
- save_string = str ( ms_pb ) if save_as_text else ms_pb . SerializeToString ( )
347
- open_mode = "w " if save_as_text else "wb "
443
+ save_dir = "../../src/main/resources/org/sonar/python/types/protobuf" if not is_debug else f"../ { debug_dir } "
444
+ save_string = ms_pb . SerializeToString ( ) if not is_debug else str ( ms_pb )
445
+ open_mode = "wb " if not is_debug else "w "
348
446
save_dir_path = os .path .join (CURRENT_PATH , save_dir )
349
447
if not os .path .exists (save_dir_path ):
350
448
os .makedirs (save_dir_path )
351
- with open (f"{ save_dir_path } /{ ms .fullname } .protobuf" , open_mode ) as f :
449
+ save_name = ms .fullname if not is_python_2_only_exception (ms ) else f"2@{ ms .fullname } "
450
+ with open (f"{ save_dir_path } /{ save_name } .protobuf" , open_mode ) as f :
352
451
f .write (save_string )
452
+
453
+
454
+ def is_python_2_only_exception (ms ) -> bool :
455
+ """ This methods aims to flag some Python 2 modules whose name differ from their Python 3 counterpart
456
+ by capitalization only. This is done to avoid conflicts in the saved file for OS which are not case sensitive
457
+ (e.g Windows and macOS)
458
+ """
459
+ if (not isinstance (ms , MergedModuleSymbol )
460
+ or ms .fullname not in ['ConfigParser' , 'Queue' , 'SocketServer' ]):
461
+ return False
462
+ return True
0 commit comments