Coverage for apio / __main__.py: 72%

35 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2025-12-24 01:53 +0000

1#!venv/bin/python 

2"""Apio starting point.""" 

3 

4import sys 

5import os 

6import atexit 

7 

8# -- Since this module is used also as the entry point for the scons 

9# -- subprocess, we don't add here dependency on util.py and use a light 

10# -- weight debug env var detection. 

11# -- Set APIO_DEBUG=1 to enable. 

12debug_enabled = int(os.environ.get("APIO_DEBUG", "0")) > 0 

13 

14 

15def on_exit(msg): 

16 """Prints a debug message on process exit. The msg string is passed 

17 from the handler registrations below. 

18 """ 

19 if debug_enabled: 19 ↛ 20line 19 didn't jump to line 20 because the condition on line 19 was never true

20 print(msg) 

21 

22 

23def main(): 

24 """Apio starting point.""" 

25 

26 if debug_enabled: 26 ↛ 27line 26 didn't jump to line 27 because the condition on line 26 was never true

27 print(f"Apio main(): original argv: {sys.argv}") 

28 

29 # pylint: disable=import-outside-toplevel 

30 

31 # -- Handle the case of the scons subprocess. Because we also use 

32 # -- pyinstaller, without standard pip packages, we can't simply invoke 

33 # -- the 'scons' binary so we invoke the SCons module from the apio main. 

34 # -- See more details at: 

35 # -- See https://github.com/orgs/pyinstaller/discussions/9023. 

36 # -- 

37 # -- In this case argv goes through these stages 

38 # -- Original: <binary> -m apio --scons ... 

39 # -- Under python: <binary> --scons ... 

40 # -- Under pyinstaller: <binary> -m apio --scons ... 

41 # -- After fixing for scons: <binary> ... 

42 # -- 

43 # -- Notice that the -m apio args are automatically removed by the 

44 # -- python interpreter but are preserved by the pyinstaller, hence 

45 # -- the two cases. 

46 python_scons = sys.argv[1:2] == ["--scons"] 

47 pyinstaller_scons = sys.argv[1:4] == ["-m", "apio", "--scons"] 

48 

49 if debug_enabled: 49 ↛ 50line 49 didn't jump to line 50 because the condition on line 49 was never true

50 print(f"Apio main(): {python_scons=}") 

51 print(f"Apio main(): {pyinstaller_scons=}") 

52 

53 if python_scons or pyinstaller_scons: 

54 

55 if debug_enabled: 55 ↛ 56line 55 didn't jump to line 56 because the condition on line 55 was never true

56 print("Apio main(): this is an scons process") 

57 

58 # -- Since scons_main() doesn't return, we use this handler to print 

59 # -- an exit message for debugging. 

60 atexit.register(on_exit, "Apio main(): scons process exit") 

61 

62 # -- Import and initialize scons only when running the scons 

63 # -- subprocess. 

64 from SCons.Script.Main import main as scons_main 

65 from apio.common.common_util import maybe_wait_for_remote_debugger 

66 

67 # -- If system env var APIO_SCONS_DEBUGGER is defined, regardless of 

68 # -- its value, we wait on a remote debugger to be attached, e.g. 

69 # -- from Visual Studio Code. 

70 # -- 

71 # -- You can place a breakpoint for example at SconsHandler.start(). 

72 maybe_wait_for_remote_debugger("APIO_SCONS_DEBUGGER") 

73 

74 # -- Drop the scons trigger args 

75 if python_scons: 75 ↛ 82line 75 didn't jump to line 82 because the condition on line 75 was always true

76 # -- Case 1: Using a python interpreter which already dropped 

77 # -- the ["-m", "apio"]. Dropping just ["--scons"] 

78 sys.argv[1:] = sys.argv[2:] 

79 else: 

80 # -- Case 2: Using pyinstaller which preserved the ["-m", "apio"]. 

81 # -- Dropping ["-m", "apio", "--scons"] 

82 sys.argv[1:] = sys.argv[4:] 

83 

84 if debug_enabled: 84 ↛ 85line 84 didn't jump to line 85 because the condition on line 84 was never true

85 print(f"Apio main(): scons fixed argv: {sys.argv}") 

86 

87 # -- Invoke the scons main function. It gets the modified argv from sys 

88 # -- and doesn't return. 

89 scons_main() 

90 

91 # -- Handle the case of a normal apio invocation. 

92 else: 

93 if debug_enabled: 93 ↛ 94line 93 didn't jump to line 94 because the condition on line 93 was never true

94 print("Apio main(): this is an apio process") 

95 

96 # -- Since apio_top_cli() doesn't return, we use this handler to print 

97 # -- an exit message for debugging. 

98 atexit.register(on_exit, "Apio main(): apio process exit") 

99 

100 # -- Import the apio CLI only when running the apio process 

101 # -- (as opposed to the scons sub process). 

102 from apio.commands.apio import apio_top_cli 

103 

104 # -- Due to the Click decorations of apio_top_cli() and the Click 

105 # -- magic, this function is not really invoked but Click dispatches 

106 # -- it to its subcommands that was selected by the user command line. 

107 # -- This function call doesn't return. 

108 apio_top_cli() 

109 

110 

111if __name__ == "__main__": 

112 main()