33# parttool is used to perform partition level operations - reading,
44# writing, erasing and getting info about the partition.
55#
6- # SPDX-FileCopyrightText: 2018-2024 Espressif Systems (Shanghai) CO LTD
6+ # SPDX-FileCopyrightText: 2018-2025 Espressif Systems (Shanghai) CO LTD
77# SPDX-License-Identifier: Apache-2.0
88import argparse
99import os
@@ -30,8 +30,7 @@ def status(msg):
3030 print (msg )
3131
3232
33- class _PartitionId ():
34-
33+ class _PartitionId :
3534 def __init__ (self , name = None , p_type = None , subtype = None , part_list = None ):
3635 self .name = name
3736 self .type = p_type
@@ -40,30 +39,39 @@ def __init__(self, name=None, p_type=None, subtype=None, part_list=None):
4039
4140
4241class PartitionName (_PartitionId ):
43-
4442 def __init__ (self , name ):
4543 _PartitionId .__init__ (self , name = name )
4644
4745
4846class PartitionType (_PartitionId ):
49-
5047 def __init__ (self , p_type , subtype , part_list = None ):
5148 _PartitionId .__init__ (self , p_type = p_type , subtype = subtype , part_list = part_list )
5249
5350
5451PARTITION_BOOT_DEFAULT = _PartitionId ()
5552
5653
57- class ParttoolTarget ():
58-
59- def __init__ (self , port = None , baud = None , partition_table_offset = PARTITION_TABLE_OFFSET , primary_bootloader_offset = None , recovery_bootloader_offset = None ,
60- partition_table_file = None , esptool_args = [], esptool_write_args = [], esptool_read_args = [], esptool_erase_args = []):
54+ class ParttoolTarget :
55+ def __init__ (
56+ self ,
57+ port = None ,
58+ baud = None ,
59+ partition_table_offset = PARTITION_TABLE_OFFSET ,
60+ primary_bootloader_offset = None ,
61+ recovery_bootloader_offset = None ,
62+ partition_table_file = None ,
63+ esptool_args = [],
64+ esptool_write_args = [],
65+ esptool_read_args = [],
66+ esptool_erase_args = [],
67+ ):
6168 self .port = port
6269 self .baud = baud
6370
6471 gen .offset_part_table = partition_table_offset
6572 gen .primary_bootloader_offset = primary_bootloader_offset
6673 gen .recovery_bootloader_offset = recovery_bootloader_offset
74+ gen .quiet = True
6775
6876 def parse_esptool_args (esptool_args ):
6977 results = list ()
@@ -84,23 +92,16 @@ def parse_esptool_args(esptool_args):
8492 self .esptool_erase_args = parse_esptool_args (esptool_erase_args )
8593
8694 if partition_table_file :
87- partition_table = None
8895 with open (partition_table_file , 'rb' ) as f :
89- input_is_binary = (f .read (2 ) == gen .PartitionDefinition .MAGIC_BYTES )
90- f .seek (0 )
91- if input_is_binary :
92- partition_table = gen .PartitionTable .from_binary (f .read ())
93-
94- if partition_table is None :
95- with open (partition_table_file , 'r' , encoding = 'utf-8' ) as f :
96- f .seek (0 )
97- partition_table = gen .PartitionTable .from_csv (f .read ())
96+ partition_table , _ = gen .PartitionTable .from_file (f )
9897 else :
9998 temp_file = tempfile .NamedTemporaryFile (delete = False )
10099 temp_file .close ()
101100
102101 try :
103- self ._call_esptool (['read_flash' , str (partition_table_offset ), str (gen .MAX_PARTITION_LENGTH ), temp_file .name ])
102+ self ._call_esptool (
103+ ['read_flash' , str (partition_table_offset ), str (gen .MAX_PARTITION_LENGTH ), temp_file .name ]
104+ )
104105 with open (temp_file .name , 'rb' ) as f :
105106 partition_table = gen .PartitionTable .from_binary (f .read ())
106107 finally :
@@ -152,30 +153,30 @@ def get_partition_info(self, partition_id):
152153
153154 def erase_partition (self , partition_id ):
154155 partition = self .get_partition_info (partition_id )
155- self ._call_esptool (['erase_region' , str (partition .offset ), str (partition .size )] + self .esptool_erase_args )
156+ self ._call_esptool (['erase_region' , str (partition .offset ), str (partition .size )] + self .esptool_erase_args )
156157
157158 def read_partition (self , partition_id , output ):
158159 partition = self .get_partition_info (partition_id )
159160 self ._call_esptool (['read_flash' , str (partition .offset ), str (partition .size ), output ] + self .esptool_read_args )
160161
161- def write_partition (self , partition_id , input , ignore_readonly = False ):
162+ def write_partition (self , partition_id , input , ignore_readonly = False ): # noqa: A002
162163 partition = self .get_partition_info (partition_id )
163164
164165 if partition .readonly and not ignore_readonly :
165166 raise SystemExit (f'"{ partition .name } " partition is read-only, (use the --ignore-readonly flag to skip it)' )
166167
167168 self .erase_partition (partition_id )
168169
169- with open (input , 'rb' ) as input_file :
170- content_len = len (input_file .read ())
170+ with open (input , 'rb' ) as f :
171+ content_len = len (f .read ())
171172
172173 if content_len > partition .size :
173174 raise Exception ('Input file size exceeds partition size' )
174175
175176 self ._call_esptool (['write_flash' , str (partition .offset ), input ] + self .esptool_write_args )
176177
177178
178- def _write_partition (target , partition_id , input , ignore_readonly = False ):
179+ def _write_partition (target , partition_id , input , ignore_readonly = False ): # noqa: A002
179180 target .write_partition (partition_id , input , ignore_readonly )
180181 partition = target .get_partition_info (partition_id )
181182 status ("Written contents of file '{}' at offset 0x{:x}" .format (input , partition .offset ))
@@ -184,8 +185,11 @@ def _write_partition(target, partition_id, input, ignore_readonly=False):
184185def _read_partition (target , partition_id , output ):
185186 target .read_partition (partition_id , output )
186187 partition = target .get_partition_info (partition_id )
187- status ("Read partition '{}' contents from device at offset 0x{:x} to file '{}'"
188- .format (partition .name , partition .offset , output ))
188+ status (
189+ "Read partition '{}' contents from device at offset 0x{:x} to file '{}'" .format (
190+ partition .name , partition .offset , output
191+ )
192+ )
189193
190194
191195def _erase_partition (target , partition_id ):
@@ -213,7 +217,7 @@ def _get_partition_info(target, partition_id, info):
213217 'offset' : '0x{:x}' .format (p .offset ),
214218 'size' : '0x{:x}' .format (p .size ),
215219 'encrypted' : '{}' .format (p .encrypted ),
216- 'readonly' : '{}' .format (p .readonly )
220+ 'readonly' : '{}' .format (p .readonly ),
217221 }
218222 for i in info :
219223 infos += [info_dict [i ]]
@@ -232,19 +236,29 @@ def main():
232236 parser .add_argument ('--esptool-args' , help = 'additional main arguments for esptool' , nargs = '+' )
233237 parser .add_argument ('--esptool-write-args' , help = 'additional subcommand arguments when writing to flash' , nargs = '+' )
234238 parser .add_argument ('--esptool-read-args' , help = 'additional subcommand arguments when reading flash' , nargs = '+' )
235- parser .add_argument ('--esptool-erase-args' , help = 'additional subcommand arguments when erasing regions of flash' , nargs = '+' )
239+ parser .add_argument (
240+ '--esptool-erase-args' , help = 'additional subcommand arguments when erasing regions of flash' , nargs = '+'
241+ )
236242
237243 # By default the device attached to the specified port is queried for the partition table. If a partition table file
238244 # is specified, that is used instead.
239- parser .add_argument ('--port' , '-p' , help = 'port where the target device of the command is connected to; the partition table is sourced from this device \
240- when the partition table file is not defined' )
245+ parser .add_argument (
246+ '--port' ,
247+ '-p' ,
248+ help = 'port where the target device of the command is connected to; the partition table is sourced from '
249+ 'this device when the partition table file is not defined' ,
250+ )
241251 parser .add_argument ('--baud' , '-b' , help = 'baudrate to use' , type = int )
242252
243253 parser .add_argument ('--partition-table-offset' , '-o' , help = 'offset to read the partition table from' , type = str )
244254 parser .add_argument ('--primary-bootloader-offset' , help = 'offset for primary bootloader' , type = str )
245255 parser .add_argument ('--recovery-bootloader-offset' , help = 'offset for recovery bootloader' , type = str )
246- parser .add_argument ('--partition-table-file' , '-f' , help = 'file (CSV/binary) to read the partition table from; \
247- overrides device attached to specified port as the partition table source when defined' )
256+ parser .add_argument (
257+ '--partition-table-file' ,
258+ '-f' ,
259+ help = 'file (CSV/binary) to read the partition table from; '
260+ 'overrides device attached to specified port as the partition table source when defined' ,
261+ )
248262
249263 partition_selection_parser = argparse .ArgumentParser (add_help = False )
250264
@@ -254,31 +268,54 @@ def main():
254268
255269 partition_selection_args .add_argument ('--partition-name' , '-n' , help = 'name of the partition' )
256270 partition_selection_args .add_argument ('--partition-type' , '-t' , help = 'type of the partition' )
257- partition_selection_args .add_argument ('--partition-boot-default' , '-d' , help = 'select the default boot partition \
258- using the same fallback logic as the IDF bootloader' , action = 'store_true' )
271+ partition_selection_args .add_argument (
272+ '--partition-boot-default' ,
273+ '-d' ,
274+ help = 'select the default boot partition \
275+ using the same fallback logic as the IDF bootloader' ,
276+ action = 'store_true' ,
277+ )
259278
260279 partition_selection_parser .add_argument ('--partition-subtype' , '-s' , help = 'subtype of the partition' )
261- partition_selection_parser .add_argument ('--extra-partition-subtypes' , help = 'Extra partition subtype entries' , nargs = '*' )
280+ partition_selection_parser .add_argument (
281+ '--extra-partition-subtypes' , help = 'Extra partition subtype entries' , nargs = '*'
282+ )
262283
263284 subparsers = parser .add_subparsers (dest = 'operation' , help = 'run parttool -h for additional help' )
264285
265286 # Specify the supported operations
266- read_part_subparser = subparsers .add_parser ('read_partition' , help = 'read partition from device and dump contents into a file' ,
267- parents = [partition_selection_parser ])
287+ read_part_subparser = subparsers .add_parser (
288+ 'read_partition' ,
289+ help = 'read partition from device and dump contents into a file' ,
290+ parents = [partition_selection_parser ],
291+ )
268292 read_part_subparser .add_argument ('--output' , help = 'file to dump the read partition contents to' )
269293
270- write_part_subparser = subparsers .add_parser ('write_partition' , help = 'write contents of a binary file to partition on device' ,
271- parents = [partition_selection_parser ])
294+ write_part_subparser = subparsers .add_parser (
295+ 'write_partition' ,
296+ help = 'write contents of a binary file to partition on device' ,
297+ parents = [partition_selection_parser ],
298+ )
272299 write_part_subparser .add_argument ('--input' , help = 'file whose contents are to be written to the partition offset' )
273300 write_part_subparser .add_argument ('--ignore-readonly' , help = 'Ignore read-only attribute' , action = 'store_true' )
274301
275- subparsers .add_parser ('erase_partition' , help = 'erase the contents of a partition on the device' , parents = [partition_selection_parser ])
276-
277- print_partition_info_subparser = subparsers .add_parser ('get_partition_info' , help = 'get partition information' , parents = [partition_selection_parser ])
278- print_partition_info_subparser .add_argument ('--info' , help = 'type of partition information to get' ,
279- choices = ['name' , 'type' , 'subtype' , 'offset' , 'size' , 'encrypted' , 'readonly' ],
280- default = ['offset' , 'size' ], nargs = '+' )
281- print_partition_info_subparser .add_argument ('--part_list' , help = 'Get a list of partitions suitable for a given type' , action = 'store_true' )
302+ subparsers .add_parser (
303+ 'erase_partition' , help = 'erase the contents of a partition on the device' , parents = [partition_selection_parser ]
304+ )
305+
306+ print_partition_info_subparser = subparsers .add_parser (
307+ 'get_partition_info' , help = 'get partition information' , parents = [partition_selection_parser ]
308+ )
309+ print_partition_info_subparser .add_argument (
310+ '--info' ,
311+ help = 'type of partition information to get' ,
312+ choices = ['name' , 'type' , 'subtype' , 'offset' , 'size' , 'encrypted' , 'readonly' ],
313+ default = ['offset' , 'size' ],
314+ nargs = '+' ,
315+ )
316+ print_partition_info_subparser .add_argument (
317+ '--part_list' , help = 'Get a list of partitions suitable for a given type' , action = 'store_true'
318+ )
282319
283320 args = parser .parse_args ()
284321 quiet = args .quiet
@@ -299,8 +336,10 @@ def main():
299336 elif args .partition_boot_default :
300337 partition_id = PARTITION_BOOT_DEFAULT
301338 else :
302- raise RuntimeError ('Partition to operate on should be defined using --partition-name OR \
303- partition-type,--partition-subtype OR partition-boot-default' )
339+ raise RuntimeError (
340+ 'Partition to operate on should be defined using --partition-name OR \
341+ partition-type,--partition-subtype OR partition-boot-default'
342+ )
304343
305344 # Prepare the device to perform operation on
306345 target_args = {}
@@ -341,18 +380,18 @@ def main():
341380 target = ParttoolTarget (** target_args )
342381
343382 # Create the operation table and execute the operation
344- common_args = {'target' :target , 'partition_id' :partition_id }
383+ common_args = {'target' : target , 'partition_id' : partition_id }
345384 parttool_ops = {
346385 'erase_partition' : (_erase_partition , []),
347386 'read_partition' : (_read_partition , ['output' ]),
348387 'write_partition' : (_write_partition , ['input' , 'ignore_readonly' ]),
349- 'get_partition_info' : (_get_partition_info , ['info' ])
388+ 'get_partition_info' : (_get_partition_info , ['info' ]),
350389 }
351390
352391 (op , op_args ) = parttool_ops [args .operation ]
353392
354393 for op_arg in op_args :
355- common_args .update ({op_arg :vars (args )[op_arg ]})
394+ common_args .update ({op_arg : vars (args )[op_arg ]})
356395
357396 if quiet :
358397 # If exceptions occur, suppress and exit quietly
0 commit comments