@@ -112,15 +112,18 @@ def _validate_partition_mappings(self, partitions: Dict[str, str] | None) -> Non
112112 f"Please provide a valid file path (e.g., -t { partition_name } :/path/to/image)"
113113 )
114114
115- def _power_off_if_available (self ) -> None :
116- """Power off device if power child is present."""
115+ def _power_off_if_available (self , power_off : bool = True ) -> None :
116+ """Power off device if power child is present and power_off is True."""
117+ if not power_off :
118+ self .logger .info ("leaving device powered on" )
119+ return
117120 if "power" in self .children :
118121 self .power .off ()
119122 self .logger .info ("device powered off" )
120123 else :
121124 self .logger .info ("device left running" )
122125
123- def _execute_flash_operation (self , operation_func , * args , ** kwargs ):
126+ def _execute_flash_operation (self , operation_func , * args , power_off : bool = True , ** kwargs ):
124127 """Common wrapper for flash operations with logging and power management."""
125128 self .logger .info ("Starting RideSX flash operation" )
126129 self .boot_to_fastboot ()
@@ -145,7 +148,7 @@ def _execute_flash_operation(self, operation_func, *args, **kwargs):
145148 )
146149
147150 try :
148- self ._power_off_if_available ()
151+ self ._power_off_if_available (power_off )
149152 except Exception as power_error :
150153 self .logger .exception ("power-off cleanup failed after flash operation error: %s" , power_error )
151154
@@ -154,7 +157,7 @@ def _execute_flash_operation(self, operation_func, *args, **kwargs):
154157 raise
155158
156159 try :
157- self ._power_off_if_available ()
160+ self ._power_off_if_available (power_off )
158161 except Exception as power_error :
159162 # Keep successful flashes successful, but make cleanup failures visible.
160163 self .logger .exception ("power-off cleanup failed after successful flash operation: %s" , power_error )
@@ -168,6 +171,7 @@ def flash(
168171 target : str | None = None ,
169172 operator : Operator | Dict [str , Operator ] | None = None ,
170173 compression = None ,
174+ power_off : bool = True ,
171175 ):
172176 """Flash image to DUT - supports both OCI and traditional paths.
173177
@@ -176,23 +180,24 @@ def flash(
176180 target: Target partition (for single file mode)
177181 operator: Optional operator for file access (usually auto-detected)
178182 compression: Compression type
183+ power_off: Whether to power off the device after flashing (default: True)
179184 """
180185 # Auto-detect flash mode based on path type
181186 if isinstance (path , dict ):
182187 # Dictionary mode: {partition: file_path, ...}
183188 operators_dict = operator if isinstance (operator , dict ) else None
184- return self .flash_local (path , operators_dict )
189+ return self .flash_local (path , operators_dict , power_off = power_off )
185190
186191 elif isinstance (path , str ) and (path .startswith ("oci://" ) or self ._is_oci_path (path )):
187192 # OCI mode: auto-detect partitions or use target as partition->filename mapping
188193 if target and ":" in target :
189194 # Target is "partition:filename" format for OCI explicit mapping
190195 partition_name , filename = target .split (":" , 1 )
191196 partitions = {partition_name : filename }
192- return self .flash_with_targets (path , partitions )
197+ return self .flash_with_targets (path , partitions , power_off = power_off )
193198 else :
194199 # OCI auto-detection mode
195- return self .flash_oci_auto (path , None )
200+ return self .flash_oci_auto (path , None , power_off = power_off )
196201
197202 else :
198203 # Traditional single file mode
@@ -211,18 +216,21 @@ def flash(
211216 operators = None
212217
213218 partitions = {target : path }
214- return self .flash_local (partitions , operators )
219+ return self .flash_local (partitions , operators , power_off = power_off )
215220
216221 def flash_with_targets (
217222 self ,
218223 oci_url : str ,
219224 partitions : Dict [str , str ],
225+ * ,
226+ power_off : bool = True ,
220227 ):
221228 """Flash OCI image with explicit partition mappings.
222229
223230 Args:
224231 oci_url: OCI image URL (must start with oci://)
225232 partitions: Mapping of partition name -> filename in OCI image
233+ power_off: Whether to power off the device after flashing (default: True)
226234
227235 Raises:
228236 ValueError: If partitions is empty or None
@@ -239,18 +247,21 @@ def flash_with_targets(
239247 def _flash_operation ():
240248 return self ._flash_oci_auto_impl (oci_url , partitions )
241249
242- return self ._execute_flash_operation (_flash_operation )
250+ return self ._execute_flash_operation (_flash_operation , power_off = power_off )
243251
244252 def flash_local (
245253 self ,
246254 partitions : Dict [str , str ],
247255 operators : Dict [str , Operator ] | None = None ,
256+ * ,
257+ power_off : bool = True ,
248258 ):
249259 """Flash local files or URLs to partitions.
250260
251261 Args:
252262 partitions: Mapping of partition name -> file path or URL
253263 operators: Optional mapping of partition name -> operator
264+ power_off: Whether to power off the device after flashing (default: True)
254265 """
255266 self ._validate_partition_mappings (partitions )
256267
@@ -259,7 +270,7 @@ def flash_local(
259270 def _flash_operation ():
260271 return self .flash_images (partitions , operators )
261272
262- return self ._execute_flash_operation (_flash_operation )
273+ return self ._execute_flash_operation (_flash_operation , power_off = power_off )
263274
264275 def _read_oci_credentials (self ):
265276 """Read OCI registry credentials from environment variables.
@@ -316,12 +327,15 @@ def flash_oci_auto(
316327 self ,
317328 oci_url : str ,
318329 partitions : Dict [str , str ] | None = None ,
330+ * ,
331+ power_off : bool = True ,
319332 ):
320333 """Flash OCI image using auto-detection or explicit partition mapping
321334
322335 Args:
323336 oci_url: OCI image reference (e.g., "oci://registry.com/image:latest")
324337 partitions: Optional mapping of partition -> filename inside OCI image
338+ power_off: Whether to power off the device after flashing (default: True)
325339 """
326340 # Normalize OCI URL
327341 if not oci_url .startswith ("oci://" ):
@@ -340,7 +354,7 @@ def flash_oci_auto(
340354 def _flash_operation ():
341355 return self ._flash_oci_auto_impl (oci_url , partitions )
342356
343- return self ._execute_flash_operation (_flash_operation )
357+ return self ._execute_flash_operation (_flash_operation , power_off = power_off )
344358
345359 def _parse_target_specs (self , target_specs : tuple [str , ...]) -> dict [str , str ]:
346360 """Parse -t target specs into a partition->path mapping."""
@@ -373,7 +387,7 @@ def _parse_and_validate_targets(self, target_specs: tuple[str, ...]):
373387
374388 return mapping , single_target
375389
376- def _execute_flash_command (self , path , target_specs ):
390+ def _execute_flash_command (self , path , target_specs , power_off : bool = True ):
377391 """Execute flash command logic with proper argument handling."""
378392 # Parse target specifications
379393 if target_specs :
@@ -382,18 +396,18 @@ def _execute_flash_command(self, path, target_specs):
382396 if mapping :
383397 if path :
384398 # Multi-partition mode with path: extract specific files from OCI image
385- self .flash_with_targets (path , mapping )
399+ self .flash_with_targets (path , mapping , power_off = power_off )
386400 else :
387401 # Multi-partition mode: use mapping as dict for local files
388- self .flash (mapping )
402+ self .flash (mapping , power_off = power_off )
389403 else :
390404 # Single partition mode: use path with target
391405 if not path :
392406 raise click .ClickException ("Path argument required when using single-partition target" )
393- self .flash (path , target = single_target )
407+ self .flash (path , target = single_target , power_off = power_off )
394408 elif path :
395409 # Path only - should be OCI for auto-detection
396- self .flash (path )
410+ self .flash (path , power_off = power_off )
397411 else :
398412 raise click .ClickException ("Provide a path or use -t to specify partition mappings" )
399413
@@ -419,7 +433,15 @@ def base():
419433 multiple = True ,
420434 help = "Target spec as partition:path for multi-partition or just partition for single file" ,
421435 )
422- def flash (path , target_specs ):
436+ @click .option (
437+ "--no-power-off" ,
438+ "power_off" ,
439+ is_flag = True ,
440+ flag_value = False ,
441+ default = True ,
442+ help = "Leave device powered on after flashing" ,
443+ )
444+ def flash (path , target_specs , power_off ):
423445 """Flash image to device.
424446
425447 \b
@@ -447,7 +469,7 @@ def flash(path, target_specs):
447469 OCI_USERNAME Registry username for private OCI images
448470 OCI_PASSWORD Registry password for private OCI images
449471 """
450- self ._execute_flash_command (path , target_specs )
472+ self ._execute_flash_command (path , target_specs , power_off = power_off )
451473
452474 @base .command ()
453475 def boot_to_fastboot ():
0 commit comments