fbuild 1.2.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. fbuild/__init__.py +390 -0
  2. fbuild/assets/example.txt +1 -0
  3. fbuild/build/__init__.py +117 -0
  4. fbuild/build/archive_creator.py +186 -0
  5. fbuild/build/binary_generator.py +444 -0
  6. fbuild/build/build_component_factory.py +131 -0
  7. fbuild/build/build_info_generator.py +624 -0
  8. fbuild/build/build_state.py +325 -0
  9. fbuild/build/build_utils.py +93 -0
  10. fbuild/build/compilation_executor.py +422 -0
  11. fbuild/build/compiler.py +165 -0
  12. fbuild/build/compiler_avr.py +574 -0
  13. fbuild/build/configurable_compiler.py +664 -0
  14. fbuild/build/configurable_linker.py +637 -0
  15. fbuild/build/flag_builder.py +214 -0
  16. fbuild/build/library_dependency_processor.py +185 -0
  17. fbuild/build/linker.py +708 -0
  18. fbuild/build/orchestrator.py +67 -0
  19. fbuild/build/orchestrator_avr.py +651 -0
  20. fbuild/build/orchestrator_esp32.py +878 -0
  21. fbuild/build/orchestrator_rp2040.py +719 -0
  22. fbuild/build/orchestrator_stm32.py +696 -0
  23. fbuild/build/orchestrator_teensy.py +580 -0
  24. fbuild/build/source_compilation_orchestrator.py +218 -0
  25. fbuild/build/source_scanner.py +516 -0
  26. fbuild/cli.py +717 -0
  27. fbuild/cli_utils.py +314 -0
  28. fbuild/config/__init__.py +16 -0
  29. fbuild/config/board_config.py +542 -0
  30. fbuild/config/board_loader.py +92 -0
  31. fbuild/config/ini_parser.py +369 -0
  32. fbuild/config/mcu_specs.py +88 -0
  33. fbuild/daemon/__init__.py +42 -0
  34. fbuild/daemon/async_client.py +531 -0
  35. fbuild/daemon/client.py +1505 -0
  36. fbuild/daemon/compilation_queue.py +293 -0
  37. fbuild/daemon/configuration_lock.py +865 -0
  38. fbuild/daemon/daemon.py +585 -0
  39. fbuild/daemon/daemon_context.py +293 -0
  40. fbuild/daemon/error_collector.py +263 -0
  41. fbuild/daemon/file_cache.py +332 -0
  42. fbuild/daemon/firmware_ledger.py +546 -0
  43. fbuild/daemon/lock_manager.py +508 -0
  44. fbuild/daemon/logging_utils.py +149 -0
  45. fbuild/daemon/messages.py +957 -0
  46. fbuild/daemon/operation_registry.py +288 -0
  47. fbuild/daemon/port_state_manager.py +249 -0
  48. fbuild/daemon/process_tracker.py +366 -0
  49. fbuild/daemon/processors/__init__.py +18 -0
  50. fbuild/daemon/processors/build_processor.py +248 -0
  51. fbuild/daemon/processors/deploy_processor.py +664 -0
  52. fbuild/daemon/processors/install_deps_processor.py +431 -0
  53. fbuild/daemon/processors/locking_processor.py +777 -0
  54. fbuild/daemon/processors/monitor_processor.py +285 -0
  55. fbuild/daemon/request_processor.py +457 -0
  56. fbuild/daemon/shared_serial.py +819 -0
  57. fbuild/daemon/status_manager.py +238 -0
  58. fbuild/daemon/subprocess_manager.py +316 -0
  59. fbuild/deploy/__init__.py +21 -0
  60. fbuild/deploy/deployer.py +67 -0
  61. fbuild/deploy/deployer_esp32.py +310 -0
  62. fbuild/deploy/docker_utils.py +315 -0
  63. fbuild/deploy/monitor.py +519 -0
  64. fbuild/deploy/qemu_runner.py +603 -0
  65. fbuild/interrupt_utils.py +34 -0
  66. fbuild/ledger/__init__.py +52 -0
  67. fbuild/ledger/board_ledger.py +560 -0
  68. fbuild/output.py +352 -0
  69. fbuild/packages/__init__.py +66 -0
  70. fbuild/packages/archive_utils.py +1098 -0
  71. fbuild/packages/arduino_core.py +412 -0
  72. fbuild/packages/cache.py +256 -0
  73. fbuild/packages/concurrent_manager.py +510 -0
  74. fbuild/packages/downloader.py +518 -0
  75. fbuild/packages/fingerprint.py +423 -0
  76. fbuild/packages/framework_esp32.py +538 -0
  77. fbuild/packages/framework_rp2040.py +349 -0
  78. fbuild/packages/framework_stm32.py +459 -0
  79. fbuild/packages/framework_teensy.py +346 -0
  80. fbuild/packages/github_utils.py +96 -0
  81. fbuild/packages/header_trampoline_cache.py +394 -0
  82. fbuild/packages/library_compiler.py +203 -0
  83. fbuild/packages/library_manager.py +549 -0
  84. fbuild/packages/library_manager_esp32.py +725 -0
  85. fbuild/packages/package.py +163 -0
  86. fbuild/packages/platform_esp32.py +383 -0
  87. fbuild/packages/platform_rp2040.py +400 -0
  88. fbuild/packages/platform_stm32.py +581 -0
  89. fbuild/packages/platform_teensy.py +312 -0
  90. fbuild/packages/platform_utils.py +131 -0
  91. fbuild/packages/platformio_registry.py +369 -0
  92. fbuild/packages/sdk_utils.py +231 -0
  93. fbuild/packages/toolchain.py +436 -0
  94. fbuild/packages/toolchain_binaries.py +196 -0
  95. fbuild/packages/toolchain_esp32.py +489 -0
  96. fbuild/packages/toolchain_metadata.py +185 -0
  97. fbuild/packages/toolchain_rp2040.py +436 -0
  98. fbuild/packages/toolchain_stm32.py +417 -0
  99. fbuild/packages/toolchain_teensy.py +404 -0
  100. fbuild/platform_configs/esp32.json +150 -0
  101. fbuild/platform_configs/esp32c2.json +144 -0
  102. fbuild/platform_configs/esp32c3.json +143 -0
  103. fbuild/platform_configs/esp32c5.json +151 -0
  104. fbuild/platform_configs/esp32c6.json +151 -0
  105. fbuild/platform_configs/esp32p4.json +149 -0
  106. fbuild/platform_configs/esp32s3.json +151 -0
  107. fbuild/platform_configs/imxrt1062.json +56 -0
  108. fbuild/platform_configs/rp2040.json +70 -0
  109. fbuild/platform_configs/rp2350.json +76 -0
  110. fbuild/platform_configs/stm32f1.json +59 -0
  111. fbuild/platform_configs/stm32f4.json +63 -0
  112. fbuild/py.typed +0 -0
  113. fbuild-1.2.8.dist-info/METADATA +468 -0
  114. fbuild-1.2.8.dist-info/RECORD +121 -0
  115. fbuild-1.2.8.dist-info/WHEEL +5 -0
  116. fbuild-1.2.8.dist-info/entry_points.txt +5 -0
  117. fbuild-1.2.8.dist-info/licenses/LICENSE +21 -0
  118. fbuild-1.2.8.dist-info/top_level.txt +2 -0
  119. fbuild_lint/__init__.py +0 -0
  120. fbuild_lint/ruff_plugins/__init__.py +0 -0
  121. fbuild_lint/ruff_plugins/keyboard_interrupt_checker.py +158 -0
@@ -0,0 +1,158 @@
1
+ """Flake8 plugin to check for proper KeyboardInterrupt handling.
2
+
3
+ This plugin ensures that try-except blocks that catch broad exceptions
4
+ (like Exception or BaseException) also properly handle KeyboardInterrupt.
5
+
6
+ Error Codes:
7
+ KBI001: Try-except catches Exception/BaseException without KeyboardInterrupt handler
8
+ KBI002: KeyboardInterrupt handler must call _thread.interrupt_main() or handle_keyboard_interrupt_properly()
9
+ """
10
+
11
+ import ast
12
+ from typing import Any, Generator, Tuple, Type
13
+
14
+
15
+ class KeyboardInterruptChecker:
16
+ """Flake8 plugin to check for proper KeyboardInterrupt handling."""
17
+
18
+ name = "keyboard-interrupt-checker"
19
+ version = "1.0.0"
20
+
21
+ def __init__(self, tree: ast.AST) -> None:
22
+ """Initialize the checker with an AST tree.
23
+
24
+ Args:
25
+ tree: The AST tree to check
26
+ """
27
+ self._tree = tree
28
+
29
+ def run(self) -> Generator[Tuple[int, int, str, Type[Any]], None, None]:
30
+ """Run the checker on the AST tree.
31
+
32
+ Yields:
33
+ Tuple of (line_number, column, message, type)
34
+ """
35
+ visitor = TryExceptVisitor()
36
+ visitor.visit(self._tree)
37
+
38
+ for line, col, msg in visitor.errors:
39
+ yield (line, col, msg, type(self))
40
+
41
+
42
+ class TryExceptVisitor(ast.NodeVisitor):
43
+ """AST visitor to check try-except blocks for KeyboardInterrupt handling."""
44
+
45
+ def __init__(self) -> None:
46
+ """Initialize the visitor."""
47
+ self.errors: list[Tuple[int, int, str]] = []
48
+
49
+ def visit_Try(self, node: ast.Try) -> None:
50
+ """Visit a Try node and check exception handlers.
51
+
52
+ Args:
53
+ node: The Try node to check
54
+ """
55
+ # Check if any handler catches Exception or BaseException
56
+ catches_broad_exception = False
57
+ has_keyboard_interrupt_handler = False
58
+ keyboard_interrupt_handlers = []
59
+
60
+ for handler in node.handlers:
61
+ if handler.type is None:
62
+ # bare except: catches everything
63
+ catches_broad_exception = True
64
+ elif isinstance(handler.type, ast.Name):
65
+ if handler.type.id in ("Exception", "BaseException"):
66
+ catches_broad_exception = True
67
+ elif handler.type.id == "KeyboardInterrupt":
68
+ has_keyboard_interrupt_handler = True
69
+ keyboard_interrupt_handlers.append(handler)
70
+ elif isinstance(handler.type, ast.Tuple):
71
+ # Check if tuple contains Exception or BaseException
72
+ for exc_type in handler.type.elts:
73
+ if isinstance(exc_type, ast.Name):
74
+ if exc_type.id in ("Exception", "BaseException"):
75
+ catches_broad_exception = True
76
+ elif exc_type.id == "KeyboardInterrupt":
77
+ has_keyboard_interrupt_handler = True
78
+ keyboard_interrupt_handlers.append(handler)
79
+
80
+ # If we catch broad exceptions without KeyboardInterrupt handler, that's an error
81
+ if catches_broad_exception and not has_keyboard_interrupt_handler:
82
+ self.errors.append(
83
+ (
84
+ node.lineno,
85
+ node.col_offset,
86
+ (
87
+ "KBI001 Try-except catches Exception/BaseException without KeyboardInterrupt handler. "
88
+ "Add: except KeyboardInterrupt as ke: handle_keyboard_interrupt_properly(ke)"
89
+ ),
90
+ )
91
+ )
92
+
93
+ # Check all KeyboardInterrupt handlers to ensure they call _thread.interrupt_main()
94
+ for keyboard_interrupt_handler in keyboard_interrupt_handlers:
95
+ if not self._handler_calls_interrupt_main(keyboard_interrupt_handler):
96
+ self.errors.append(
97
+ (
98
+ keyboard_interrupt_handler.lineno,
99
+ keyboard_interrupt_handler.col_offset,
100
+ (
101
+ "KBI002 KeyboardInterrupt handler must call _thread.interrupt_main() "
102
+ "or use handle_keyboard_interrupt_properly(). "
103
+ "Add: import _thread; _thread.interrupt_main()"
104
+ ),
105
+ )
106
+ )
107
+
108
+ # Continue visiting child nodes
109
+ self.generic_visit(node)
110
+
111
+ def _handler_calls_interrupt_main(self, handler: ast.ExceptHandler) -> bool:
112
+ """Check if a KeyboardInterrupt handler properly calls _thread.interrupt_main().
113
+
114
+ Args:
115
+ handler: The exception handler to check
116
+
117
+ Returns:
118
+ True if the handler calls _thread.interrupt_main(), handle_keyboard_interrupt_properly(),
119
+ sys.exit(), ErrorFormatter.handle_keyboard_interrupt(), or re-raises the exception
120
+ """
121
+ # Check for re-raise (bare raise statement)
122
+ for node in ast.walk(handler):
123
+ if isinstance(node, ast.Raise):
124
+ # Bare raise (re-raise) is allowed
125
+ if node.exc is None:
126
+ return True
127
+
128
+ # Check for calls to _thread.interrupt_main(), handle_keyboard_interrupt_properly(), or sys.exit()
129
+ for node in ast.walk(handler):
130
+ if isinstance(node, ast.Call):
131
+ # Check for _thread.interrupt_main()
132
+ if isinstance(node.func, ast.Attribute):
133
+ if (
134
+ isinstance(node.func.value, ast.Name)
135
+ and node.func.value.id == "_thread"
136
+ and node.func.attr == "interrupt_main"
137
+ ):
138
+ return True
139
+ # Check for sys.exit()
140
+ if (
141
+ isinstance(node.func.value, ast.Name)
142
+ and node.func.value.id == "sys"
143
+ and node.func.attr == "exit"
144
+ ):
145
+ return True
146
+ # Check for ErrorFormatter.handle_keyboard_interrupt() or similar
147
+ if node.func.attr == "handle_keyboard_interrupt":
148
+ return True
149
+
150
+ # Check for handle_keyboard_interrupt_properly()
151
+ if isinstance(node.func, ast.Name):
152
+ if node.func.id == "handle_keyboard_interrupt_properly":
153
+ return True
154
+ # Check for handle_keyboard_interrupt()
155
+ if node.func.id == "handle_keyboard_interrupt":
156
+ return True
157
+
158
+ return False