Bug#1140927: depthcharge-tools: Traceback with 3.14: args for positionals must be != 0

Cyril Brulebois kibi at debian.org
Sun Jun 28 14:35:38 BST 2026


Package: depthcharge-tools
Version: 0.6.2-4
Severity: serious
Tags: d-i
Justification: working depthchargectl invokations break all of a sudden, breaks d-i
X-Debbugs-Cc: debian-boot at lists.debian.org, debian-kernel at lists.debian.org, reproducible-builds at lists.alioth.debian.org

Hi,

The python3-defaults package was just switched from 3.13 to 3.14 and
that breaks debian-installer builds. Previously, that's been working
fine for a very long time:

    update-manifest dest/cdrom/gtk/debian-cd_info.tar.gz "isolinux help screens for CD (graphical)"               
    pigz -nm -v -d <tmp/cdrom_gtk/initrd.gz |   xz -T0 --check=crc32 --block-size=1M -v >tmp/cdrom_gtk/initrd.xz  
    <stdin> to <stdout>                                                                                           
    (stdin): 57.1 MiB / 134.4 MiB = 0.425, 14 MiB/s, 0:09                                                         
    rm -f ./tmp/cdrom_gtk/depthcharge/*                                                                           
    mkdir -p ./tmp/cdrom_gtk/depthcharge                                                                          
    for board in amd64-generic; do \                                                                              
            depthchargectl build -v \                                                                             
                    --board ${board} \                                                                            
                    --kernel-release 7.0.13+deb14-amd64 \                                                         
                    --kernel ./tmp/cdrom_gtk/vmlinuz \                                                            
                    --initramfs ./tmp/cdrom_gtk/initrd.xz \                                                       
                    --root none \                                                                                 
                    --kernel-cmdline "partman-partitioning/default_label=gpt --- quiet" \                         
                    --output ./tmp/cdrom_gtk/depthcharge/${board}.kernel.img; \                                   
            gen-hd-image -v -z \                                                                                  
                    -X gpt \                                                                                      
                    -p $(($(stat -c%s ./tmp/cdrom_gtk/depthcharge/${board}.kernel.img) / 512 + 2048)) \           
                    -i $(mktemp -d) \                                                                             
                    -o ./tmp/cdrom_gtk/depthcharge/${board}.disk.img \                                            
                    -d FE3A2A5D-4F32-41A7-B725-ACCC3285A309 \                                                     
                    ./tmp/cdrom_gtk/depthcharge/${board}.kernel.img \                                             
                    2048; \                                                                                       
            pigz -9nmf ./tmp/cdrom_gtk/depthcharge/${board}.kernel.img; \                                         
    done                                                                                                          
    Couldn't find /boot in fstab, falling back to '/boot'.                                                        
    Assuming board 'Unnamed amd64-generic board' ('amd64-generic') by codename argument or config.                
    Using kernel cmdline from given options: partman-partitioning/default_label=gpt --- quiet                     
    Building depthcharge image for board 'Unnamed amd64-generic board' ('amd64-generic').                         
    Building for kernel version '7.0.13+deb14-amd64'.                                                             
    Trying with compression 'none'.                                                                               
    Using file '/build/reproducible-path/debian-installer-20250804/build/tmp/cdrom_gtk/vmlinuz' as a vmlinuz.     
    Trying to decompress file '/build/reproducible-path/debian-installer-20250804/build/tmp/cdrom_gtk/vmlinuz'.   
    Using file '/build/reproducible-path/debian-installer-20250804/build/tmp/cdrom_gtk/initrd.xz' as an initramfs.
    Using keyblock file '/usr/share/vboot/devkeys/kernel.keyblock'.                                               
    Using signprivate file '/usr/share/vboot/devkeys/kernel_data_key.vbprivk'.                                    
    Using signpubkey file '/usr/share/vboot/devkeys/kernel_subkey.vbpubk'.                                        
    Vmlinuz pref_address is 0x1000000, with init_size 0x3c47000.                                                  
    Padding vmlinuz to size 0xdaa000                                                                              
    Packing files as temporary image.                                                                             
    […]

That broke all of a sudden with a traceback:

    update-manifest dest/cdrom/gtk/debian-cd_info.tar.gz "isolinux help screens for CD (graphical)"              
    pigz -nm -v -d <tmp/cdrom_gtk/initrd.gz |   xz -T0 --check=crc32 --block-size=1M -v >tmp/cdrom_gtk/initrd.xz 
    <stdin> to <stdout>                                                                                          
    (stdin): 57.1 MiB / 134.4 MiB = 0.425, 14 MiB/s, 0:09                                                        
    rm -f ./tmp/cdrom_gtk/depthcharge/*                                                                          
    mkdir -p ./tmp/cdrom_gtk/depthcharge                                                                         
    for board in amd64-generic; do \                                                                             
            depthchargectl build -v \                                                                            
                    --board ${board} \                                                                           
                    --kernel-release 7.0.13+deb14-amd64 \                                                        
                    --kernel ./tmp/cdrom_gtk/vmlinuz \                                                           
                    --initramfs ./tmp/cdrom_gtk/initrd.xz \                                                      
                    --root none \                                                                                
                    --kernel-cmdline "partman-partitioning/default_label=gpt --- quiet" \                        
                    --output ./tmp/cdrom_gtk/depthcharge/${board}.kernel.img; \                                  
            gen-hd-image -v -z \                                                                                 
                    -X gpt \                                                                                     
                    -p $(($(stat -c%s ./tmp/cdrom_gtk/depthcharge/${board}.kernel.img) / 512 + 2048)) \          
                    -i $(mktemp -d) \                                                                            
                    -o ./tmp/cdrom_gtk/depthcharge/${board}.disk.img \                                           
                    -d FE3A2A5D-4F32-41A7-B725-ACCC3285A309 \                                                    
                    ./tmp/cdrom_gtk/depthcharge/${board}.kernel.img \                                            
                    2048; \                                                                                      
            pigz -9nmf ./tmp/cdrom_gtk/depthcharge/${board}.kernel.img; \                                        
    done                                                                                                         
 →  Traceback (most recent call last):                                                                           
 →    file "/usr/bin/depthchargectl", line 33, in <module>                                                       
 →      sys.exit(load_entry_point('depthcharge-tools==0.6.2', 'console_scripts', 'depthchargectl')())            
 →               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^             
 →    File "/usr/lib/python3/dist-packages/depthcharge_tools/utils/argparse.py", line 895, in main               
 →      parser = cls.parser                                                                                      
 →               ^^^^^^^^^^                                                                                      
 →    File "/usr/lib/python3/dist-packages/depthcharge_tools/utils/argparse.py", line 1042, in parser            
 →      return cls.__build()                                                                                     
 →             ~~~~~~~~~~~^^                                                                                     
 →    File "/usr/lib/python3/dist-packages/depthcharge_tools/utils/argparse.py", line 1065, in __build           
 →      arg.build(parser)                                                                                        
 →      ~~~~~~~~~^^^^^^^^                                                                                        
 →    File "/usr/lib/python3/dist-packages/depthcharge_tools/utils/argparse.py", line 439, in build              
 →      return parent.add_argument(*option_strings, **kwargs)                                                    
 →             ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^                                                    
 →    File "/usr/lib/python3.14/argparse.py", line 1531, in add_argument                                         
 →      kwargs = self._get_positional_kwargs(*args, **kwargs)                                                    
 →    File "/usr/lib/python3.14/argparse.py", line 1666, in _get_positional_kwargs                               
 →      raise ValueError('nargs for positionals must be != 0')                                                   
 →  ValueError: nargs for positionals must be != 0                                                               
    stat: cannot statx './tmp/cdrom_gtk/depthcharge/amd64-generic.kernel.img': No such file or directory         
    /bin/sh: 10: arithmetic expression: expecting primary: " / 512 + 2048"                                       
    make[4]: *** [config/x86.cfg:437: arch_depthcharge] Error 2                                                  
    make[3]: *** [Makefile:315: _build] Error 2                                                                  
    make[2]: *** [Makefile:309: build_cdrom_gtk] Error 2                                                         
    make[1]: *** [Makefile:315: _build] Error 2                                                                  
    make: *** [Makefile:309: build_cdrom_isolinux] Error 2

(The parts after the traceback can be ignored, that's definitely a
problem in the installer's Makefile.)

I didn't immediately spot a breaking change in the “what's new in 3.14”
page, or the argparse documentation, but a quick search in CPython's git
repository quickly returned the following:

    commit 9944ad388c457325456152257b977410c4ec3593
    Author: Serhiy Storchaka <storchaka at gmail.com>
    Date:   Sat Oct 12 16:04:17 2024 +0300
    
        gh-85935: Check for nargs=0 for positional arguments in argparse (GH-124839)
        
        Raise ValueError in add_argument() if either explicit nargs=0 or action
        that does not consume arguments (like 'store_const' or 'store_true') is
        specified for positional argument.
    
    diff --git a/Lib/argparse.py b/Lib/argparse.py
    index cbecb3b753c..550415dc934 100644
    --- a/Lib/argparse.py
    +++ b/Lib/argparse.py
    @@ -1441,11 +1441,17 @@ def add_argument(self, *args, **kwargs):
                     kwargs['default'] = self.argument_default
     
             # create the action object, and add it to the parser
    +        action_name = kwargs.get('action')
             action_class = self._pop_action_class(kwargs)
             if not callable(action_class):
                 raise ValueError('unknown action "%s"' % (action_class,))
             action = action_class(**kwargs)
     
    +        # raise an error if action for positional argument does not
    +        # consume arguments
    +        if not action.option_strings and action.nargs == 0:
    +            raise ValueError(f'action {action_name!r} is not valid for positional arguments')
    +
             # raise an error if the action type is not callable
             type_func = self._registry_get('type', action.type, action.type)
             if not callable(type_func):
    @@ -1554,7 +1560,9 @@ def _get_positional_kwargs(self, dest, **kwargs):
             # mark positional arguments as required if at least one is
             # always required
             nargs = kwargs.get('nargs')
    -        if nargs not in [OPTIONAL, ZERO_OR_MORE, REMAINDER, SUPPRESS, 0]:
    +        if nargs == 0:
    +            raise ValueError('nargs for positionals must be != 0')
    +        if nargs not in [OPTIONAL, ZERO_OR_MORE, REMAINDER, SUPPRESS]:
                 kwargs['required'] = True
     
             # return the keyword arguments with no option strings

And that's be consistent with the 3.13 versus 3.14 hypothesis:

    kibi at tokyo:~/hack/cpython.git (main =)$ git describe --contains --tags 9944ad388c457325456152257b977410c4ec3593
    v3.14.0a1~62


Cheers,
-- 
Cyril Brulebois (kibi at debian.org)            <https://debamax.com/>
D-I release manager -- Release team member -- Freelance Consultant


More information about the Reproducible-builds mailing list