3
3
import ast
4
4
import re
5
5
import json
6
+ import argparse
7
+ import subprocess
8
+ import sys
9
+ import threading
10
+
11
+ # Argument parser for command line options
12
+ parser = argparse .ArgumentParser (description = "Analyze Python project for classes and keyboard shortcuts." )
13
+ parser .add_argument ("-v" , "--verbose" , action = "store_true" , help = "Display all messages" )
14
+ args = parser .parse_args ()
15
+
16
+ def verbose_print (message ):
17
+ """Prints messages only if verbose mode is enabled."""
18
+ if args .verbose :
19
+ print (message )
6
20
7
21
def find_keypress_functions_with_kbkeys (project_path ):
8
22
result = {}
@@ -121,16 +135,16 @@ def find_class_inheritance(classes):
121
135
cls ["inherits_from" ] = None
122
136
123
137
def display_classes (classes ):
124
- print ("\n Classes Found in Files:" )
138
+ verbose_print ("\n Classes Found in Files:" )
125
139
for entry in classes :
126
- print (f"File: { os .path .basename (entry ['file' ])} , Class: { entry ['class_name' ]} , "
140
+ verbose_print (f"File: { os .path .basename (entry ['file' ])} , Class: { entry ['class_name' ]} , "
127
141
f"Start Line: { entry ['start_line' ]} , End Line: { entry ['end_line' ]} , "
128
142
f"Uses: { entry ['uses' ]} , Inherits from: { entry ['inherits_from' ]} " )
129
143
130
144
def display_results (results ):
131
- print ("\n \n Found keypress functions with kbkey references:" )
145
+ verbose_print ("\n \n Found keypress functions with kbkey references:" )
132
146
for class_name , kbkeys in results .items ():
133
- print (f"Class: { class_name } , kbkey References: { kbkeys } " )
147
+ verbose_print (f"Class: { class_name } , kbkey References: { kbkeys } " )
134
148
135
149
def map_functional_relationships (results , classes ):
136
150
"""Map functional relationships of classes with keypress shortcuts."""
@@ -180,24 +194,25 @@ def functional_structural_integration(results, classes):
180
194
return integration_map
181
195
182
196
def display_functional_relationships (map_data ):
183
- print ("\n Functional Relationships:" )
184
- for entry in map_data :
185
- print (f"Class: { entry ['class_name' ]} , Inherits From: { entry ['inherits_from' ]} , "
186
- f"Uses: { entry ['uses' ]} , Keypress Shortcuts: { entry ['kbkeys' ]} " )
197
+ if args .verbose :
198
+ verbose_print ("\n Functional Relationships:" )
199
+ for entry in map_data :
200
+ verbose_print (f"Class: { entry ['class_name' ]} , Inherits From: { entry ['inherits_from' ]} , "
201
+ f"Uses: { entry ['uses' ]} , Keypress Shortcuts: { entry ['kbkeys' ]} " )
187
202
188
203
def display_dependencies (map_data ):
189
- print ("\n Dependencies Analysis:" )
190
- for entry in map_data :
191
- print (f"Class: { entry ['class_name' ]} , Depends On: { entry ['dependencies' ]} , "
192
- f"Own Keypress Shortcuts: { entry ['kbkeys' ]} " )
204
+ if args .verbose :
205
+ verbose_print ("\n Dependencies Analysis:" )
206
+ for entry in map_data :
207
+ verbose_print (f"Class: { entry ['class_name' ]} , Depends On: { entry ['dependencies' ]} , "
208
+ f"Own Keypress Shortcuts: { entry ['kbkeys' ]} " )
193
209
194
210
def display_integration (integration_map ):
195
- print ("\n Functional-Structural Integration:" )
196
- for cls , data in integration_map .items ():
197
- print (f"Class: { cls } , Keypress Shortcuts: { data ['kbkeys' ]} , "
198
- f"Related Classes: { data ['related_classes' ]} " )
199
-
200
-
211
+ if args .verbose :
212
+ verbose_print ("\n Functional-Structural Integration:" )
213
+ for cls , data in integration_map .items ():
214
+ verbose_print (f"Class: { cls } , Keypress Shortcuts: { data ['kbkeys' ]} , "
215
+ f"Related Classes: { data ['related_classes' ]} " )
201
216
202
217
def extend_results_with_related_keys (results , classes ):
203
218
"""Extend results by appending keys from used and inherited classes."""
@@ -225,16 +240,16 @@ def extend_results_with_related_keys(results, classes):
225
240
return extended_results
226
241
227
242
def display_extended_results (extended_results ):
228
- print ("\n Extended Results with Related Keys:" )
229
- for class_name , keys in extended_results .items ():
230
- print (f"Class: { class_name } , Keypress Shortcuts: { keys } " )
243
+ if args .verbose :
244
+ verbose_print ("\n Extended Results with Related Keys:" )
245
+ for class_name , keys in extended_results .items ():
246
+ verbose_print (f"Class: { class_name } , Keypress Shortcuts: { keys } " )
231
247
232
248
def extract_global_functions_keys (file_path ):
233
249
"""Extract keys from `_global_functions` in the specified Python file."""
234
250
global_functions_keys = []
235
251
inside_global_functions = False
236
252
inside_local_functions = False
237
- global_functions_keys = []
238
253
local_functions_keys = []
239
254
file_name = os .path .join (file_path , 'radio.py' )
240
255
@@ -325,17 +340,49 @@ def precompute_context_map(results):
325
340
context_map [key ].append (class_name )
326
341
return context_map
327
342
343
+ def ask_and_execute ():
344
+ print ("Do you want to execute './pyradio/keyboard.py'? (y/n, ENTER = 'y'): " , end = '' , flush = True )
345
+
346
+ # Variable to store user's answer
347
+ user_answer = None
348
+ input_event = threading .Event ()
349
+
350
+ def get_user_input ():
351
+ nonlocal user_answer
352
+ user_answer = input ().strip ().lower ()
353
+ input_event .set () # Signal that input was received
354
+
355
+ # Start a thread to get the user input
356
+ input_thread = threading .Thread (target = get_user_input )
357
+ input_thread .daemon = True
358
+ input_thread .start ()
359
+
360
+ # Wait for input or timeout
361
+ if input_event .wait (timeout = 5 ): # Wait up to 5 seconds for input
362
+ if user_answer == '' or user_answer == 'y' : # Treat ENTER or 'y' as confirmation
363
+ try :
364
+ # Execute the script
365
+ subprocess .run (["python" , "./pyradio/keyboard.py" ], check = True )
366
+ except subprocess .CalledProcessError as e :
367
+ print (f"Error executing script: { e } " )
368
+ except FileNotFoundError :
369
+ print ("File './pyradio/keyboard.py' not found." )
370
+ else :
371
+ print ("You entered 'n'. Exiting..." )
372
+ else :
373
+ print ("\n Timeout occurred. Exiting..." )
374
+
328
375
if __name__ == "__main__" :
329
376
from sys import exit
330
377
# Updated project path
331
378
starting_dir = os .getcwd ()
332
- print (f'{ starting_dir = } ' )
379
+ verbose_print (f'{ starting_dir = } ' )
333
380
project_path = os .path .join (starting_dir , 'pyradio' )
334
- print (f'{ project_path = } ' )
381
+ verbose_print (f'{ project_path = } ' )
335
382
out_file = os .path .join (project_path , 'keyboard' , 'classes.json' )
336
383
new_out_file = os .path .join (project_path , 'keyboard' , 'keys.json' )
337
- print (f'{ out_file = } ' )
338
- print (f'{ new_out_file = } ' )
384
+ verbose_print (f'{ out_file = } ' )
385
+ verbose_print (f'{ new_out_file = } ' )
339
386
340
387
# Find and display keypress functions with kbkeys
341
388
results = find_keypress_functions_with_kbkeys (project_path )
@@ -346,9 +393,9 @@ def precompute_context_map(results):
346
393
find_class_usages (classes )
347
394
find_class_inheritance (classes )
348
395
global_functions_keys , local_functions_keys = extract_global_functions_keys (project_path )
349
- results ['PyRadio' ] += local_functions_keys
396
+ results ['PyRadio' ]. extend ( local_functions_keys )
350
397
'''
351
- print (results)
398
+ verbose_print (results)
352
399
for a_class in results:
353
400
results[a_class] += global_functions_keys
354
401
'''
@@ -378,13 +425,13 @@ def precompute_context_map(results):
378
425
379
426
# Extract h_extra keys from keyboard.py
380
427
h_extra_keys = extract_section_keys (project_path , 'h_extra' )
381
- print ("\n Extracted h_extra keys:" )
428
+ verbose_print ("\n Extracted h_extra keys:" )
382
429
for key in sorted (h_extra_keys ):
383
- print (f" - { key } " )
430
+ verbose_print (f" - { key } " )
384
431
385
432
# Remove h_extra keys from results
386
433
remove_section_keys_from_results (results , h_extra_keys )
387
- print ("\n Updated results after removing h_extra keys:" )
434
+ verbose_print ("\n Updated results after removing h_extra keys:" )
388
435
display_results (results )
389
436
390
437
# these three keys are added by code, they are not detected
@@ -396,12 +443,8 @@ def precompute_context_map(results):
396
443
# info_rename is a uniq key in the info window
397
444
results ['PyRadio' ].pop (results ['PyRadio' ].index ('info_rename' ))
398
445
results ['InfoWindow' ] = ['info_rename' ]
399
-
400
- results ['GlobalFunctions' ] = global_functions_keys + ['t' ]
401
- results ['ExtraKeys' ] = list (h_extra_keys )
402
- print ("\n \n Final results after removing h_extra keys:" )
403
- display_results (results )
404
446
447
+ '''
405
448
# remove global function keys
406
449
for gl_key in global_functions_keys:
407
450
for a_key in results:
@@ -411,25 +454,34 @@ def precompute_context_map(results):
411
454
break
412
455
except ValueError:
413
456
pass
414
-
457
+ # results[a_key].extend(global_functions_keys)
458
+ '''
459
+ for a_key in results :
460
+ results [a_key ].extend (global_functions_keys )
461
+ results ['ExtraKeys' ] = list (h_extra_keys )
462
+
463
+ verbose_print ("\n \n Final results after removing h_extra keys:" )
464
+ display_results (results )
465
+
415
466
with open (out_file , 'w' , encoding = 'utf-8' ) as f :
416
467
json .dump (results , f )
417
468
418
-
419
469
precompute_map = precompute_context_map (results )
420
470
421
471
with open (new_out_file , 'w' , encoding = 'utf-8' ) as f :
422
472
json .dump (precompute_map , f )
423
- # print('\n\n{}'.format(global_functions_keys))
424
-
425
- # print ('\n\n{}'.format(h_extra_keys))
473
+
474
+ # verbose_print('\n\n{}'.format(global_functions_keys))
475
+ # verbose_print ('\n\n{}'.format(h_extra_keys))
426
476
427
477
print ('''
428
-
429
478
Files created:
430
- classes.json
431
- keys.json
479
+ - classes.json
480
+ - keys.json
432
481
Execute
433
- python keyboard.py
482
+ - python keyboard.py
434
483
to check for missing shortcuts
435
484
''' )
485
+
486
+ ask_and_execute ()
487
+ sys .exit ()
0 commit comments