Bug 2057363 - mkosi fails to build with Python 3.11: KeyError: 'ManifestFormat.json'
Summary: mkosi fails to build with Python 3.11: KeyError: 'ManifestFormat.json'
Keywords:
Status: CLOSED RAWHIDE
Alias: None
Product: Fedora
Classification: Fedora
Component: mkosi
Version: rawhide
Hardware: Unspecified
OS: Unspecified
unspecified
unspecified
Target Milestone: ---
Assignee: Zbigniew Jędrzejewski-Szmek
QA Contact: Fedora Extras Quality Assurance
URL:
Whiteboard:
Depends On:
Blocks: PYTHON3.11
TreeView+ depends on / blocked
 
Reported: 2022-02-23 09:56 UTC by Tomáš Hrnčiar
Modified: 2022-04-24 13:43 UTC (History)
4 users (show)

Fixed In Version: 12-3
Doc Type: If docs needed, set a value
Doc Text:
Clone Of:
Environment:
Last Closed: 2022-04-24 13:43:46 UTC
Type: Bug
Embargoed:


Attachments (Terms of Use)

Description Tomáš Hrnčiar 2022-02-23 09:56:28 UTC
mkosi fails to build with Python 3.11.0a4.


=================================== FAILURES ===================================
_______________________ test_def[MkosiConfigManyParams] ________________________

cls = <enum 'ManifestFormat'>, name = 'ManifestFormat.json'

    @classmethod
    def from_string(cls: Any, name: str) -> Any:
        """A convenience method to be used with argparse"""
        try:
>           return cls[name]

mkosi/backend.py:97: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <enum 'ManifestFormat'>, name = 'ManifestFormat.json'

    def __getitem__(cls, name):
        """
        Return the member matching `name`.
        """
>       return cls._member_map_[name]
E       KeyError: 'ManifestFormat.json'

/usr/lib64/python3.11/enum.py:818: KeyError

During handling of the above exception, another exception occurred:

self = ArgumentParserMkosi(prog='mkosi', usage=None, description='Build Bespoke OS Images', formatter_class=<class 'mkosi.CustomHelpFormatter'>, conflict_handler='error', add_help=False)
action = CommaDelimitedListAction(option_strings=['--manifest-format'], dest='manifest_format', nargs=None, const=None, default...ype=<bound method Parseable.parse_list of <enum 'ManifestFormat'>>, choices=None, help='Manifest Format', metavar=None)
arg_string = 'ManifestFormat.json'

    def _get_value(self, action, arg_string):
        type_func = self._registry_get('type', action.type, action.type)
        if not callable(type_func):
            msg = _('%r is not callable')
            raise ArgumentError(action, msg % type_func)
    
        # convert the value to the appropriate type
        try:
>           result = type_func(arg_string)

/usr/lib64/python3.11/argparse.py:2500: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <enum 'ManifestFormat'>, string = 'ManifestFormat.json'

    @classmethod
    def parse_list(cls: Any, string: str) -> List[Any]:
>       return [cls.from_string(p) for p in string.split(",") if p]

mkosi/backend.py:103: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

.0 = <list_iterator object at 0x7f11704a1030>

>   return [cls.from_string(p) for p in string.split(",") if p]

mkosi/backend.py:103: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <enum 'ManifestFormat'>, name = 'ManifestFormat.json'

    @classmethod
    def from_string(cls: Any, name: str) -> Any:
        """A convenience method to be used with argparse"""
        try:
            return cls[name]
        except KeyError:
>           raise argparse.ArgumentTypeError(f"unknown Format: {name!r}")
E           argparse.ArgumentTypeError: unknown Format: 'ManifestFormat.json'

mkosi/backend.py:99: ArgumentTypeError

During handling of the above exception, another exception occurred:

self = ArgumentParserMkosi(prog='mkosi', usage=None, description='Build Bespoke OS Images', formatter_class=<class 'mkosi.CustomHelpFormatter'>, conflict_handler='error', add_help=False)
args = ['@/tmp/pytest-of-mockbuild/pytest-0/test_def_MkosiConfigManyParams0/mkosi.default', '--', 'build']
namespace = <[AttributeError("'int' object has no attribute 'name'") raised in repr()] Namespace object at 0x7f116fe03950>

    def parse_known_args(self, args=None, namespace=None):
        if args is None:
            # args default to the system args
            args = _sys.argv[1:]
        else:
            # make sure that args are mutable
            args = list(args)
    
        # default Namespace built from parser defaults
        if namespace is None:
            namespace = Namespace()
    
        # add any action defaults that aren't present
        for action in self._actions:
            if action.dest is not SUPPRESS:
                if not hasattr(namespace, action.dest):
                    if action.default is not SUPPRESS:
                        setattr(namespace, action.dest, action.default)
    
        # add any parser defaults that aren't present
        for dest in self._defaults:
            if not hasattr(namespace, dest):
                setattr(namespace, dest, self._defaults[dest])
    
        # parse the arguments and exit if there are any errors
        if self.exit_on_error:
            try:
>               namespace, args = self._parse_known_args(args, namespace)

/usr/lib64/python3.11/argparse.py:1877: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ArgumentParserMkosi(prog='mkosi', usage=None, description='Build Bespoke OS Images', formatter_class=<class 'mkosi.CustomHelpFormatter'>, conflict_handler='error', add_help=False)
arg_strings = ['--distribution', 'fedora', '--release', '28', '--repositories', 'http://fedora/repos', ...]
namespace = <[AttributeError("'int' object has no attribute 'name'") raised in repr()] Namespace object at 0x7f116fe03950>

    def _parse_known_args(self, arg_strings, namespace):
        # replace arg strings that are file references
        if self.fromfile_prefix_chars is not None:
            arg_strings = self._read_args_from_files(arg_strings)
    
        # map all mutually exclusive arguments to the other arguments
        # they can't occur with
        action_conflicts = {}
        for mutex_group in self._mutually_exclusive_groups:
            group_actions = mutex_group._group_actions
            for i, mutex_action in enumerate(mutex_group._group_actions):
                conflicts = action_conflicts.setdefault(mutex_action, [])
                conflicts.extend(group_actions[:i])
                conflicts.extend(group_actions[i + 1:])
    
        # find all option indices, and determine the arg_string_pattern
        # which has an 'O' if there is an option at an index,
        # an 'A' if there is an argument, or a '-' if there is a '--'
        option_string_indices = {}
        arg_string_pattern_parts = []
        arg_strings_iter = iter(arg_strings)
        for i, arg_string in enumerate(arg_strings_iter):
    
            # all args after -- are non-options
            if arg_string == '--':
                arg_string_pattern_parts.append('-')
                for arg_string in arg_strings_iter:
                    arg_string_pattern_parts.append('A')
    
            # otherwise, add the arg to the arg strings
            # and note the index if it was an option
            else:
                option_tuple = self._parse_optional(arg_string)
                if option_tuple is None:
                    pattern = 'A'
                else:
                    option_string_indices[i] = option_tuple
                    pattern = 'O'
                arg_string_pattern_parts.append(pattern)
    
        # join the pieces together to form the pattern
        arg_strings_pattern = ''.join(arg_string_pattern_parts)
    
        # converts arg strings to the appropriate and then takes the action
        seen_actions = set()
        seen_non_default_actions = set()
    
        def take_action(action, argument_strings, option_string=None):
            seen_actions.add(action)
            argument_values = self._get_values(action, argument_strings)
    
            # error if this argument is not allowed with other previously
            # seen arguments, assuming that actions that use the default
            # value don't really count as "present"
            if argument_values is not action.default:
                seen_non_default_actions.add(action)
                for conflict_action in action_conflicts.get(action, []):
                    if conflict_action in seen_non_default_actions:
                        msg = _('not allowed with argument %s')
                        action_name = _get_action_name(conflict_action)
                        raise ArgumentError(action, msg % action_name)
    
            # take the action if we didn't receive a SUPPRESS value
            # (e.g. from a default)
            if argument_values is not SUPPRESS:
                action(self, namespace, argument_values, option_string)
    
        # function to convert arg_strings into an optional action
        def consume_optional(start_index):
    
            # get the optional identified at this index
            option_tuple = option_string_indices[start_index]
            action, option_string, explicit_arg = option_tuple
    
            # identify additional optionals in the same arg string
            # (e.g. -xyz is the same as -x -y -z if no args are required)
            match_argument = self._match_argument
            action_tuples = []
            while True:
    
                # if we found no optional action, skip it
                if action is None:
                    extras.append(arg_strings[start_index])
                    return start_index + 1
    
                # if there is an explicit argument, try to match the
                # optional's string arguments to only this
                if explicit_arg is not None:
                    arg_count = match_argument(action, 'A')
    
                    # if the action is a single-dash option and takes no
                    # arguments, try to parse more single-dash options out
                    # of the tail of the option string
                    chars = self.prefix_chars
                    if arg_count == 0 and option_string[1] not in chars:
                        action_tuples.append((action, [], option_string))
                        char = option_string[0]
                        option_string = char + explicit_arg[0]
                        new_explicit_arg = explicit_arg[1:] or None
                        optionals_map = self._option_string_actions
                        if option_string in optionals_map:
                            action = optionals_map[option_string]
                            explicit_arg = new_explicit_arg
                        else:
                            msg = _('ignored explicit argument %r')
                            raise ArgumentError(action, msg % explicit_arg)
    
                    # if the action expect exactly one argument, we've
                    # successfully matched the option; exit the loop
                    elif arg_count == 1:
                        stop = start_index + 1
                        args = [explicit_arg]
                        action_tuples.append((action, args, option_string))
                        break
    
                    # error if a double-dash option did not use the
                    # explicit argument
                    else:
                        msg = _('ignored explicit argument %r')
                        raise ArgumentError(action, msg % explicit_arg)
    
                # if there is no explicit argument, try to match the
                # optional's string arguments with the following strings
                # if successful, exit the loop
                else:
                    start = start_index + 1
                    selected_patterns = arg_strings_pattern[start:]
                    arg_count = match_argument(action, selected_patterns)
                    stop = start + arg_count
                    args = arg_strings[start:stop]
                    action_tuples.append((action, args, option_string))
                    break
    
            # add the Optional to the list and return the index at which
            # the Optional's string args stopped
            assert action_tuples
            for action, args, option_string in action_tuples:
                take_action(action, args, option_string)
            return stop
    
        # the list of Positionals left to be parsed; this is modified
        # by consume_positionals()
        positionals = self._get_positional_actions()
    
        # function to convert arg_strings into positional actions
        def consume_positionals(start_index):
            # match as many Positionals as possible
            match_partial = self._match_arguments_partial
            selected_pattern = arg_strings_pattern[start_index:]
            arg_counts = match_partial(positionals, selected_pattern)
    
            # slice off the appropriate arg strings for each Positional
            # and add the Positional and its args to the list
            for action, arg_count in zip(positionals, arg_counts):
                args = arg_strings[start_index: start_index + arg_count]
                start_index += arg_count
                take_action(action, args)
    
            # slice off the Positionals that we just parsed and return the
            # index at which the Positionals' string args stopped
            positionals[:] = positionals[len(arg_counts):]
            return start_index
    
        # consume Positionals and Optionals alternately, until we have
        # passed the last option string
        extras = []
        start_index = 0
        if option_string_indices:
            max_option_string_index = max(option_string_indices)
        else:
            max_option_string_index = -1
        while start_index <= max_option_string_index:
    
            # consume any Positionals preceding the next option
            next_option_string_index = min([
                index
                for index in option_string_indices
                if index >= start_index])
            if start_index != next_option_string_index:
                positionals_end_index = consume_positionals(start_index)
    
                # only try to parse the next optional if we didn't consume
                # the option string during the positionals parsing
                if positionals_end_index > start_index:
                    start_index = positionals_end_index
                    continue
                else:
                    start_index = positionals_end_index
    
            # if we consumed all the positionals we could and we're not
            # at the index of an option string, there were extra arguments
            if start_index not in option_string_indices:
                strings = arg_strings[start_index:next_option_string_index]
                extras.extend(strings)
                start_index = next_option_string_index
    
            # consume the next optional and any arguments for it
>           start_index = consume_optional(start_index)

/usr/lib64/python3.11/argparse.py:2085: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

start_index = 16

    def consume_optional(start_index):
    
        # get the optional identified at this index
        option_tuple = option_string_indices[start_index]
        action, option_string, explicit_arg = option_tuple
    
        # identify additional optionals in the same arg string
        # (e.g. -xyz is the same as -x -y -z if no args are required)
        match_argument = self._match_argument
        action_tuples = []
        while True:
    
            # if we found no optional action, skip it
            if action is None:
                extras.append(arg_strings[start_index])
                return start_index + 1
    
            # if there is an explicit argument, try to match the
            # optional's string arguments to only this
            if explicit_arg is not None:
                arg_count = match_argument(action, 'A')
    
                # if the action is a single-dash option and takes no
                # arguments, try to parse more single-dash options out
                # of the tail of the option string
                chars = self.prefix_chars
                if arg_count == 0 and option_string[1] not in chars:
                    action_tuples.append((action, [], option_string))
                    char = option_string[0]
                    option_string = char + explicit_arg[0]
                    new_explicit_arg = explicit_arg[1:] or None
                    optionals_map = self._option_string_actions
                    if option_string in optionals_map:
                        action = optionals_map[option_string]
                        explicit_arg = new_explicit_arg
                    else:
                        msg = _('ignored explicit argument %r')
                        raise ArgumentError(action, msg % explicit_arg)
    
                # if the action expect exactly one argument, we've
                # successfully matched the option; exit the loop
                elif arg_count == 1:
                    stop = start_index + 1
                    args = [explicit_arg]
                    action_tuples.append((action, args, option_string))
                    break
    
                # error if a double-dash option did not use the
                # explicit argument
                else:
                    msg = _('ignored explicit argument %r')
                    raise ArgumentError(action, msg % explicit_arg)
    
            # if there is no explicit argument, try to match the
            # optional's string arguments with the following strings
            # if successful, exit the loop
            else:
                start = start_index + 1
                selected_patterns = arg_strings_pattern[start:]
                arg_count = match_argument(action, selected_patterns)
                stop = start + arg_count
                args = arg_strings[start:stop]
                action_tuples.append((action, args, option_string))
                break
    
        # add the Optional to the list and return the index at which
        # the Optional's string args stopped
        assert action_tuples
        for action, args, option_string in action_tuples:
>           take_action(action, args, option_string)

/usr/lib64/python3.11/argparse.py:2025: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

action = CommaDelimitedListAction(option_strings=['--manifest-format'], dest='manifest_format', nargs=None, const=None, default...ype=<bound method Parseable.parse_list of <enum 'ManifestFormat'>>, choices=None, help='Manifest Format', metavar=None)
argument_strings = ['ManifestFormat.json'], option_string = '--manifest-format'

    def take_action(action, argument_strings, option_string=None):
        seen_actions.add(action)
>       argument_values = self._get_values(action, argument_strings)

/usr/lib64/python3.11/argparse.py:1937: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ArgumentParserMkosi(prog='mkosi', usage=None, description='Build Bespoke OS Images', formatter_class=<class 'mkosi.CustomHelpFormatter'>, conflict_handler='error', add_help=False)
action = CommaDelimitedListAction(option_strings=['--manifest-format'], dest='manifest_format', nargs=None, const=None, default...ype=<bound method Parseable.parse_list of <enum 'ManifestFormat'>>, choices=None, help='Manifest Format', metavar=None)
arg_strings = ['ManifestFormat.json']

    def _get_values(self, action, arg_strings):
        # for everything but PARSER, REMAINDER args, strip out first '--'
        if action.nargs not in [PARSER, REMAINDER]:
            try:
                arg_strings.remove('--')
            except ValueError:
                pass
    
        # optional argument produces a default when not present
        if not arg_strings and action.nargs == OPTIONAL:
            if action.option_strings:
                value = action.const
            else:
                value = action.default
            if isinstance(value, str):
                value = self._get_value(action, value)
                self._check_value(action, value)
    
        # when nargs='*' on a positional, if there were no command-line
        # args, use the default if it is anything other than None
        elif (not arg_strings and action.nargs == ZERO_OR_MORE and
              not action.option_strings):
            if action.default is not None:
                value = action.default
            else:
                value = arg_strings
            self._check_value(action, value)
    
        # single argument or optional argument produces a single value
        elif len(arg_strings) == 1 and action.nargs in [None, OPTIONAL]:
            arg_string, = arg_strings
>           value = self._get_value(action, arg_string)

/usr/lib64/python3.11/argparse.py:2467: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ArgumentParserMkosi(prog='mkosi', usage=None, description='Build Bespoke OS Images', formatter_class=<class 'mkosi.CustomHelpFormatter'>, conflict_handler='error', add_help=False)
action = CommaDelimitedListAction(option_strings=['--manifest-format'], dest='manifest_format', nargs=None, const=None, default...ype=<bound method Parseable.parse_list of <enum 'ManifestFormat'>>, choices=None, help='Manifest Format', metavar=None)
arg_string = 'ManifestFormat.json'

    def _get_value(self, action, arg_string):
        type_func = self._registry_get('type', action.type, action.type)
        if not callable(type_func):
            msg = _('%r is not callable')
            raise ArgumentError(action, msg % type_func)
    
        # convert the value to the appropriate type
        try:
            result = type_func(arg_string)
    
        # ArgumentTypeErrors indicate errors
        except ArgumentTypeError as err:
            name = getattr(action.type, '__name__', repr(action.type))
            msg = str(err)
>           raise ArgumentError(action, msg)
E           argparse.ArgumentError: argument --manifest-format: unknown Format: 'ManifestFormat.json'

/usr/lib64/python3.11/argparse.py:2506: ArgumentError

During handling of the above exception, another exception occurred:

tested_config = <tests.test_config_parser.MkosiConfigManyParams object at 0x7f116fa76bd0>
tmpdir = local('/tmp/pytest-of-mockbuild/pytest-0/test_def_MkosiConfigManyParams0')

    def test_def(tested_config, tmpdir):
        """Generate the mkosi.default file only"""
        with change_cwd(tmpdir.strpath):
            tested_config.prepare_mkosi_default(tmpdir.strpath)
>           args = mkosi.parse_args(tested_config.cli_arguments)

tests/test_config_parser.py:914: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
mkosi/__init__.py:5689: in parse_args
    args = parse_args_file_group(argv, os.fspath(default_path))
mkosi/__init__.py:5743: in parse_args_file_group
    return create_parser().parse_args(defaults_files + argv)
/usr/lib64/python3.11/argparse.py:1844: in parse_args
    args, argv = self.parse_known_args(args, namespace)
/usr/lib64/python3.11/argparse.py:1879: in parse_known_args
    self.error(str(err))
/usr/lib64/python3.11/argparse.py:2599: in error
    self.exit(2, _('%(prog)s: error: %(message)s\n') % args)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ArgumentParserMkosi(prog='mkosi', usage=None, description='Build Bespoke OS Images', formatter_class=<class 'mkosi.CustomHelpFormatter'>, conflict_handler='error', add_help=False)
status = 2
message = "mkosi: error: argument --manifest-format: unknown Format: 'ManifestFormat.json'\n"

    def exit(self, status=0, message=None):
        if message:
            self._print_message(message, _sys.stderr)
>       _sys.exit(status)
E       SystemExit: 2

/usr/lib64/python3.11/argparse.py:2586: SystemExit
----------------------------- Captured stderr call -----------------------------
usage: mkosi [-h] [--version]
             [-d {fedora,debian,ubuntu,arch,opensuse,mageia,centos,centos_epel,clear,photon,openmandriva,rocky,rocky_epel,alma,alma_epel,gentoo}]
             [-r RELEASE] [-m MIRROR] [--repositories REPOS]
             [--use-host-repositories [USE_HOST_REPOSITORIES]]
             [--architecture ARCHITECTURE]
             [-t {OutputFormat.directory,OutputFormat.subvolume,OutputFormat.tar,OutputFormat.cpio,OutputFormat.gpt_ext4,OutputFormat.gpt_xfs,OutputFormat.gpt_btrfs,OutputFormat.gpt_squashfs,OutputFormat.plain_squashfs}]
             [--manifest-format MANIFEST_FORMAT] [-o PATH]
             [--output-split-root PATH] [--output-split-verity PATH]
             [--output-split-verity-sig PATH] [--output-split-kernel PATH]
             [-O DIR] [--workspace-dir DIR] [-f] [-b [BOOTABLE]]
             [--boot-protocols PROTOCOLS]
             [--kernel-command-line KERNEL_COMMAND_LINE]
             [--secure-boot [SECURE_BOOT]] [--secure-boot-key PATH]
             [--secure-boot-certificate PATH] [--secure-boot-valid-days DAYS]
             [--secure-boot-common-name CN] [--read-only [READ_ONLY]]
             [--encrypt {all,data}] [--verity [VERITY]] [--compress [ALG]]
             [--compress-fs [ALG]] [--compress-output [ALG]]
             [--mksquashfs MKSQUASHFS_TOOL] [--qcow2 [QCOW2]]
             [--hostname HOSTNAME] [--image-version IMAGE_VERSION]
             [--image-id IMAGE_ID] [--no-chown [NO_CHOWN]]
             [--tar-strip-selinux-context [TAR_STRIP_SELINUX_CONTEXT]]
             [-i [INCREMENTAL]] [-M [MINIMIZE]]
             [--without-unified-kernel-images [WITH_UNIFIED_KERNEL_IMAGES]]
             [--gpt-first-lba FIRSTLBA] [--hostonly-initrd [HOSTONLY_INITRD]]
             [--split-artifacts [SPLIT_ARTIFACTS]] [--base-packages OPTION]
             [-p PACKAGE] [--remove-package PACKAGE] [--with-docs [WITH_DOCS]]
             [-T [WITH_TESTS]] [--password PASSWORD]
             [--password-is-hashed [PASSWORD_IS_HASHED]]
             [--autologin [AUTOLOGIN]] [--cache PATH] [--extra-tree PATH]
             [--skeleton-tree PATH]
             [--clean-package-metadata [CLEAN_PACKAGE_METADATA]]
             [--remove-files GLOB] [--environment NAME[=VALUE]]
             [--build-sources PATH] [--build-directory PATH]
             [--include-directory PATH] [--install-directory PATH]
             [--build-package PACKAGE] [--skip-final-phase [SKIP_FINAL_PHASE]]
             [--build-script PATH] [--prepare-script PATH]
             [--postinst-script PATH] [--finalize-script PATH]
             [--source-file-transfer {copy-all,copy-git-cached,copy-git-others,copy-git-more,mount,None}]
             [--source-file-transfer-final {copy-all,copy-git-cached,copy-git-others,copy-git-more,mount,None}]
             [--source-resolve-symlinks [SOURCE_RESOLVE_SYMLINKS]]
             [--source-resolve-symlinks-final [SOURCE_RESOLVE_SYMLINKS_FINAL]]
             [--with-network [WITH_NETWORK]] [--settings PATH]
             [--base-image IMAGE] [--root-size BYTES] [--esp-size BYTES]
             [--xbootldr-size BYTES] [--swap-size BYTES] [--home-size BYTES]
             [--srv-size BYTES] [--var-size BYTES] [--tmp-size BYTES]
             [--usr-only [USR_ONLY]] [--checksum [CHECKSUM]] [--sign [SIGN]]
             [--key KEY] [--bmap [BMAP]]
             [--extra-search-path EXTRA_SEARCH_PATHS]
             [--qemu-headless [QEMU_HEADLESS]] [--qemu-smp SMP]
             [--qemu-mem MEM] [--network-veth [NETWORK_VETH]]
             [--ephemeral [EPHEMERAL]] [--ssh [SSH]] [--ssh-key PATH]
             [--ssh-timeout SECONDS] [--ssh-agent PATH] [--ssh-port PORT]
             [-C PATH] [--default PATH] [-a] [--all-directory PATH]
             [-B [AUTO_BUMP]]
             [--debug {run,build-script,workspace-command,disk}]
             {build,clean,help,summary,genkey,bump,serve,build,shell,boot,qemu,ssh}
             ...
mkosi: error: argument --manifest-format: unknown Format: 'ManifestFormat.json'
______________________ test_def_1[MkosiConfigManyParams] _______________________

cls = <enum 'ManifestFormat'>, name = 'ManifestFormat.json'

    @classmethod
    def from_string(cls: Any, name: str) -> Any:
        """A convenience method to be used with argparse"""
        try:
>           return cls[name]

mkosi/backend.py:97: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <enum 'ManifestFormat'>, name = 'ManifestFormat.json'

    def __getitem__(cls, name):
        """
        Return the member matching `name`.
        """
>       return cls._member_map_[name]
E       KeyError: 'ManifestFormat.json'

/usr/lib64/python3.11/enum.py:818: KeyError

During handling of the above exception, another exception occurred:

self = ArgumentParserMkosi(prog='mkosi', usage=None, description='Build Bespoke OS Images', formatter_class=<class 'mkosi.CustomHelpFormatter'>, conflict_handler='error', add_help=False)
action = CommaDelimitedListAction(option_strings=['--manifest-format'], dest='manifest_format', nargs=None, const=None, default...ype=<bound method Parseable.parse_list of <enum 'ManifestFormat'>>, choices=None, help='Manifest Format', metavar=None)
arg_string = 'ManifestFormat.json'

    def _get_value(self, action, arg_string):
        type_func = self._registry_get('type', action.type, action.type)
        if not callable(type_func):
            msg = _('%r is not callable')
            raise ArgumentError(action, msg % type_func)
    
        # convert the value to the appropriate type
        try:
>           result = type_func(arg_string)

/usr/lib64/python3.11/argparse.py:2500: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <enum 'ManifestFormat'>, string = 'ManifestFormat.json'

    @classmethod
    def parse_list(cls: Any, string: str) -> List[Any]:
>       return [cls.from_string(p) for p in string.split(",") if p]

mkosi/backend.py:103: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

.0 = <list_iterator object at 0x7f116f4774c0>

>   return [cls.from_string(p) for p in string.split(",") if p]

mkosi/backend.py:103: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <enum 'ManifestFormat'>, name = 'ManifestFormat.json'

    @classmethod
    def from_string(cls: Any, name: str) -> Any:
        """A convenience method to be used with argparse"""
        try:
            return cls[name]
        except KeyError:
>           raise argparse.ArgumentTypeError(f"unknown Format: {name!r}")
E           argparse.ArgumentTypeError: unknown Format: 'ManifestFormat.json'

mkosi/backend.py:99: ArgumentTypeError

During handling of the above exception, another exception occurred:

self = ArgumentParserMkosi(prog='mkosi', usage=None, description='Build Bespoke OS Images', formatter_class=<class 'mkosi.CustomHelpFormatter'>, conflict_handler='error', add_help=False)
args = ['@/tmp/pytest-of-mockbuild/pytest-0/test_def_1_MkosiConfigManyPara0/mkosi.default', '@mkosi.default.d/001_mkosi.conf', '--', 'build']
namespace = <[AttributeError("'int' object has no attribute 'name'") raised in repr()] Namespace object at 0x7f116fde5190>

    def parse_known_args(self, args=None, namespace=None):
        if args is None:
            # args default to the system args
            args = _sys.argv[1:]
        else:
            # make sure that args are mutable
            args = list(args)
    
        # default Namespace built from parser defaults
        if namespace is None:
            namespace = Namespace()
    
        # add any action defaults that aren't present
        for action in self._actions:
            if action.dest is not SUPPRESS:
                if not hasattr(namespace, action.dest):
                    if action.default is not SUPPRESS:
                        setattr(namespace, action.dest, action.default)
    
        # add any parser defaults that aren't present
        for dest in self._defaults:
            if not hasattr(namespace, dest):
                setattr(namespace, dest, self._defaults[dest])
    
        # parse the arguments and exit if there are any errors
        if self.exit_on_error:
            try:
>               namespace, args = self._parse_known_args(args, namespace)

/usr/lib64/python3.11/argparse.py:1877: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ArgumentParserMkosi(prog='mkosi', usage=None, description='Build Bespoke OS Images', formatter_class=<class 'mkosi.CustomHelpFormatter'>, conflict_handler='error', add_help=False)
arg_strings = ['--distribution', 'fedora', '--release', '28', '--repositories', 'http://fedora/repos', ...]
namespace = <[AttributeError("'int' object has no attribute 'name'") raised in repr()] Namespace object at 0x7f116fde5190>

    def _parse_known_args(self, arg_strings, namespace):
        # replace arg strings that are file references
        if self.fromfile_prefix_chars is not None:
            arg_strings = self._read_args_from_files(arg_strings)
    
        # map all mutually exclusive arguments to the other arguments
        # they can't occur with
        action_conflicts = {}
        for mutex_group in self._mutually_exclusive_groups:
            group_actions = mutex_group._group_actions
            for i, mutex_action in enumerate(mutex_group._group_actions):
                conflicts = action_conflicts.setdefault(mutex_action, [])
                conflicts.extend(group_actions[:i])
                conflicts.extend(group_actions[i + 1:])
    
        # find all option indices, and determine the arg_string_pattern
        # which has an 'O' if there is an option at an index,
        # an 'A' if there is an argument, or a '-' if there is a '--'
        option_string_indices = {}
        arg_string_pattern_parts = []
        arg_strings_iter = iter(arg_strings)
        for i, arg_string in enumerate(arg_strings_iter):
    
            # all args after -- are non-options
            if arg_string == '--':
                arg_string_pattern_parts.append('-')
                for arg_string in arg_strings_iter:
                    arg_string_pattern_parts.append('A')
    
            # otherwise, add the arg to the arg strings
            # and note the index if it was an option
            else:
                option_tuple = self._parse_optional(arg_string)
                if option_tuple is None:
                    pattern = 'A'
                else:
                    option_string_indices[i] = option_tuple
                    pattern = 'O'
                arg_string_pattern_parts.append(pattern)
    
        # join the pieces together to form the pattern
        arg_strings_pattern = ''.join(arg_string_pattern_parts)
    
        # converts arg strings to the appropriate and then takes the action
        seen_actions = set()
        seen_non_default_actions = set()
    
        def take_action(action, argument_strings, option_string=None):
            seen_actions.add(action)
            argument_values = self._get_values(action, argument_strings)
    
            # error if this argument is not allowed with other previously
            # seen arguments, assuming that actions that use the default
            # value don't really count as "present"
            if argument_values is not action.default:
                seen_non_default_actions.add(action)
                for conflict_action in action_conflicts.get(action, []):
                    if conflict_action in seen_non_default_actions:
                        msg = _('not allowed with argument %s')
                        action_name = _get_action_name(conflict_action)
                        raise ArgumentError(action, msg % action_name)
    
            # take the action if we didn't receive a SUPPRESS value
            # (e.g. from a default)
            if argument_values is not SUPPRESS:
                action(self, namespace, argument_values, option_string)
    
        # function to convert arg_strings into an optional action
        def consume_optional(start_index):
    
            # get the optional identified at this index
            option_tuple = option_string_indices[start_index]
            action, option_string, explicit_arg = option_tuple
    
            # identify additional optionals in the same arg string
            # (e.g. -xyz is the same as -x -y -z if no args are required)
            match_argument = self._match_argument
            action_tuples = []
            while True:
    
                # if we found no optional action, skip it
                if action is None:
                    extras.append(arg_strings[start_index])
                    return start_index + 1
    
                # if there is an explicit argument, try to match the
                # optional's string arguments to only this
                if explicit_arg is not None:
                    arg_count = match_argument(action, 'A')
    
                    # if the action is a single-dash option and takes no
                    # arguments, try to parse more single-dash options out
                    # of the tail of the option string
                    chars = self.prefix_chars
                    if arg_count == 0 and option_string[1] not in chars:
                        action_tuples.append((action, [], option_string))
                        char = option_string[0]
                        option_string = char + explicit_arg[0]
                        new_explicit_arg = explicit_arg[1:] or None
                        optionals_map = self._option_string_actions
                        if option_string in optionals_map:
                            action = optionals_map[option_string]
                            explicit_arg = new_explicit_arg
                        else:
                            msg = _('ignored explicit argument %r')
                            raise ArgumentError(action, msg % explicit_arg)
    
                    # if the action expect exactly one argument, we've
                    # successfully matched the option; exit the loop
                    elif arg_count == 1:
                        stop = start_index + 1
                        args = [explicit_arg]
                        action_tuples.append((action, args, option_string))
                        break
    
                    # error if a double-dash option did not use the
                    # explicit argument
                    else:
                        msg = _('ignored explicit argument %r')
                        raise ArgumentError(action, msg % explicit_arg)
    
                # if there is no explicit argument, try to match the
                # optional's string arguments with the following strings
                # if successful, exit the loop
                else:
                    start = start_index + 1
                    selected_patterns = arg_strings_pattern[start:]
                    arg_count = match_argument(action, selected_patterns)
                    stop = start + arg_count
                    args = arg_strings[start:stop]
                    action_tuples.append((action, args, option_string))
                    break
    
            # add the Optional to the list and return the index at which
            # the Optional's string args stopped
            assert action_tuples
            for action, args, option_string in action_tuples:
                take_action(action, args, option_string)
            return stop
    
        # the list of Positionals left to be parsed; this is modified
        # by consume_positionals()
        positionals = self._get_positional_actions()
    
        # function to convert arg_strings into positional actions
        def consume_positionals(start_index):
            # match as many Positionals as possible
            match_partial = self._match_arguments_partial
            selected_pattern = arg_strings_pattern[start_index:]
            arg_counts = match_partial(positionals, selected_pattern)
    
            # slice off the appropriate arg strings for each Positional
            # and add the Positional and its args to the list
            for action, arg_count in zip(positionals, arg_counts):
                args = arg_strings[start_index: start_index + arg_count]
                start_index += arg_count
                take_action(action, args)
    
            # slice off the Positionals that we just parsed and return the
            # index at which the Positionals' string args stopped
            positionals[:] = positionals[len(arg_counts):]
            return start_index
    
        # consume Positionals and Optionals alternately, until we have
        # passed the last option string
        extras = []
        start_index = 0
        if option_string_indices:
            max_option_string_index = max(option_string_indices)
        else:
            max_option_string_index = -1
        while start_index <= max_option_string_index:
    
            # consume any Positionals preceding the next option
            next_option_string_index = min([
                index
                for index in option_string_indices
                if index >= start_index])
            if start_index != next_option_string_index:
                positionals_end_index = consume_positionals(start_index)
    
                # only try to parse the next optional if we didn't consume
                # the option string during the positionals parsing
                if positionals_end_index > start_index:
                    start_index = positionals_end_index
                    continue
                else:
                    start_index = positionals_end_index
    
            # if we consumed all the positionals we could and we're not
            # at the index of an option string, there were extra arguments
            if start_index not in option_string_indices:
                strings = arg_strings[start_index:next_option_string_index]
                extras.extend(strings)
                start_index = next_option_string_index
    
            # consume the next optional and any arguments for it
>           start_index = consume_optional(start_index)

/usr/lib64/python3.11/argparse.py:2085: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

start_index = 16

    def consume_optional(start_index):
    
        # get the optional identified at this index
        option_tuple = option_string_indices[start_index]
        action, option_string, explicit_arg = option_tuple
    
        # identify additional optionals in the same arg string
        # (e.g. -xyz is the same as -x -y -z if no args are required)
        match_argument = self._match_argument
        action_tuples = []
        while True:
    
            # if we found no optional action, skip it
            if action is None:
                extras.append(arg_strings[start_index])
                return start_index + 1
    
            # if there is an explicit argument, try to match the
            # optional's string arguments to only this
            if explicit_arg is not None:
                arg_count = match_argument(action, 'A')
    
                # if the action is a single-dash option and takes no
                # arguments, try to parse more single-dash options out
                # of the tail of the option string
                chars = self.prefix_chars
                if arg_count == 0 and option_string[1] not in chars:
                    action_tuples.append((action, [], option_string))
                    char = option_string[0]
                    option_string = char + explicit_arg[0]
                    new_explicit_arg = explicit_arg[1:] or None
                    optionals_map = self._option_string_actions
                    if option_string in optionals_map:
                        action = optionals_map[option_string]
                        explicit_arg = new_explicit_arg
                    else:
                        msg = _('ignored explicit argument %r')
                        raise ArgumentError(action, msg % explicit_arg)
    
                # if the action expect exactly one argument, we've
                # successfully matched the option; exit the loop
                elif arg_count == 1:
                    stop = start_index + 1
                    args = [explicit_arg]
                    action_tuples.append((action, args, option_string))
                    break
    
                # error if a double-dash option did not use the
                # explicit argument
                else:
                    msg = _('ignored explicit argument %r')
                    raise ArgumentError(action, msg % explicit_arg)
    
            # if there is no explicit argument, try to match the
            # optional's string arguments with the following strings
            # if successful, exit the loop
            else:
                start = start_index + 1
                selected_patterns = arg_strings_pattern[start:]
                arg_count = match_argument(action, selected_patterns)
                stop = start + arg_count
                args = arg_strings[start:stop]
                action_tuples.append((action, args, option_string))
                break
    
        # add the Optional to the list and return the index at which
        # the Optional's string args stopped
        assert action_tuples
        for action, args, option_string in action_tuples:
>           take_action(action, args, option_string)

/usr/lib64/python3.11/argparse.py:2025: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

action = CommaDelimitedListAction(option_strings=['--manifest-format'], dest='manifest_format', nargs=None, const=None, default...ype=<bound method Parseable.parse_list of <enum 'ManifestFormat'>>, choices=None, help='Manifest Format', metavar=None)
argument_strings = ['ManifestFormat.json'], option_string = '--manifest-format'

    def take_action(action, argument_strings, option_string=None):
        seen_actions.add(action)
>       argument_values = self._get_values(action, argument_strings)

/usr/lib64/python3.11/argparse.py:1937: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ArgumentParserMkosi(prog='mkosi', usage=None, description='Build Bespoke OS Images', formatter_class=<class 'mkosi.CustomHelpFormatter'>, conflict_handler='error', add_help=False)
action = CommaDelimitedListAction(option_strings=['--manifest-format'], dest='manifest_format', nargs=None, const=None, default...ype=<bound method Parseable.parse_list of <enum 'ManifestFormat'>>, choices=None, help='Manifest Format', metavar=None)
arg_strings = ['ManifestFormat.json']

    def _get_values(self, action, arg_strings):
        # for everything but PARSER, REMAINDER args, strip out first '--'
        if action.nargs not in [PARSER, REMAINDER]:
            try:
                arg_strings.remove('--')
            except ValueError:
                pass
    
        # optional argument produces a default when not present
        if not arg_strings and action.nargs == OPTIONAL:
            if action.option_strings:
                value = action.const
            else:
                value = action.default
            if isinstance(value, str):
                value = self._get_value(action, value)
                self._check_value(action, value)
    
        # when nargs='*' on a positional, if there were no command-line
        # args, use the default if it is anything other than None
        elif (not arg_strings and action.nargs == ZERO_OR_MORE and
              not action.option_strings):
            if action.default is not None:
                value = action.default
            else:
                value = arg_strings
            self._check_value(action, value)
    
        # single argument or optional argument produces a single value
        elif len(arg_strings) == 1 and action.nargs in [None, OPTIONAL]:
            arg_string, = arg_strings
>           value = self._get_value(action, arg_string)

/usr/lib64/python3.11/argparse.py:2467: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ArgumentParserMkosi(prog='mkosi', usage=None, description='Build Bespoke OS Images', formatter_class=<class 'mkosi.CustomHelpFormatter'>, conflict_handler='error', add_help=False)
action = CommaDelimitedListAction(option_strings=['--manifest-format'], dest='manifest_format', nargs=None, const=None, default...ype=<bound method Parseable.parse_list of <enum 'ManifestFormat'>>, choices=None, help='Manifest Format', metavar=None)
arg_string = 'ManifestFormat.json'

    def _get_value(self, action, arg_string):
        type_func = self._registry_get('type', action.type, action.type)
        if not callable(type_func):
            msg = _('%r is not callable')
            raise ArgumentError(action, msg % type_func)
    
        # convert the value to the appropriate type
        try:
            result = type_func(arg_string)
    
        # ArgumentTypeErrors indicate errors
        except ArgumentTypeError as err:
            name = getattr(action.type, '__name__', repr(action.type))
            msg = str(err)
>           raise ArgumentError(action, msg)
E           argparse.ArgumentError: argument --manifest-format: unknown Format: 'ManifestFormat.json'

/usr/lib64/python3.11/argparse.py:2506: ArgumentError

During handling of the above exception, another exception occurred:

tested_config = <tests.test_config_parser.MkosiConfigManyParams object at 0x7f116f1eac10>
tmpdir = local('/tmp/pytest-of-mockbuild/pytest-0/test_def_1_MkosiConfigManyPara0')

    def test_def_1(tested_config, tmpdir):
        """Generate the mkosi.default file plus one config file"""
        with change_cwd(tmpdir.strpath):
            tested_config.prepare_mkosi_default(tmpdir.strpath)
            tested_config.prepare_mkosi_default_d_1(tmpdir.strpath)
>           args = mkosi.parse_args(tested_config.cli_arguments)

tests/test_config_parser.py:923: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
mkosi/__init__.py:5689: in parse_args
    args = parse_args_file_group(argv, os.fspath(default_path))
mkosi/__init__.py:5743: in parse_args_file_group
    return create_parser().parse_args(defaults_files + argv)
/usr/lib64/python3.11/argparse.py:1844: in parse_args
    args, argv = self.parse_known_args(args, namespace)
/usr/lib64/python3.11/argparse.py:1879: in parse_known_args
    self.error(str(err))
/usr/lib64/python3.11/argparse.py:2599: in error
    self.exit(2, _('%(prog)s: error: %(message)s\n') % args)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ArgumentParserMkosi(prog='mkosi', usage=None, description='Build Bespoke OS Images', formatter_class=<class 'mkosi.CustomHelpFormatter'>, conflict_handler='error', add_help=False)
status = 2
message = "mkosi: error: argument --manifest-format: unknown Format: 'ManifestFormat.json'\n"

    def exit(self, status=0, message=None):
        if message:
            self._print_message(message, _sys.stderr)
>       _sys.exit(status)
E       SystemExit: 2

/usr/lib64/python3.11/argparse.py:2586: SystemExit
----------------------------- Captured stderr call -----------------------------
usage: mkosi [-h] [--version]
             [-d {fedora,debian,ubuntu,arch,opensuse,mageia,centos,centos_epel,clear,photon,openmandriva,rocky,rocky_epel,alma,alma_epel,gentoo}]
             [-r RELEASE] [-m MIRROR] [--repositories REPOS]
             [--use-host-repositories [USE_HOST_REPOSITORIES]]
             [--architecture ARCHITECTURE]
             [-t {OutputFormat.directory,OutputFormat.subvolume,OutputFormat.tar,OutputFormat.cpio,OutputFormat.gpt_ext4,OutputFormat.gpt_xfs,OutputFormat.gpt_btrfs,OutputFormat.gpt_squashfs,OutputFormat.plain_squashfs}]
             [--manifest-format MANIFEST_FORMAT] [-o PATH]
             [--output-split-root PATH] [--output-split-verity PATH]
             [--output-split-verity-sig PATH] [--output-split-kernel PATH]
             [-O DIR] [--workspace-dir DIR] [-f] [-b [BOOTABLE]]
             [--boot-protocols PROTOCOLS]
             [--kernel-command-line KERNEL_COMMAND_LINE]
             [--secure-boot [SECURE_BOOT]] [--secure-boot-key PATH]
             [--secure-boot-certificate PATH] [--secure-boot-valid-days DAYS]
             [--secure-boot-common-name CN] [--read-only [READ_ONLY]]
             [--encrypt {all,data}] [--verity [VERITY]] [--compress [ALG]]
             [--compress-fs [ALG]] [--compress-output [ALG]]
             [--mksquashfs MKSQUASHFS_TOOL] [--qcow2 [QCOW2]]
             [--hostname HOSTNAME] [--image-version IMAGE_VERSION]
             [--image-id IMAGE_ID] [--no-chown [NO_CHOWN]]
             [--tar-strip-selinux-context [TAR_STRIP_SELINUX_CONTEXT]]
             [-i [INCREMENTAL]] [-M [MINIMIZE]]
             [--without-unified-kernel-images [WITH_UNIFIED_KERNEL_IMAGES]]
             [--gpt-first-lba FIRSTLBA] [--hostonly-initrd [HOSTONLY_INITRD]]
             [--split-artifacts [SPLIT_ARTIFACTS]] [--base-packages OPTION]
             [-p PACKAGE] [--remove-package PACKAGE] [--with-docs [WITH_DOCS]]
             [-T [WITH_TESTS]] [--password PASSWORD]
             [--password-is-hashed [PASSWORD_IS_HASHED]]
             [--autologin [AUTOLOGIN]] [--cache PATH] [--extra-tree PATH]
             [--skeleton-tree PATH]
             [--clean-package-metadata [CLEAN_PACKAGE_METADATA]]
             [--remove-files GLOB] [--environment NAME[=VALUE]]
             [--build-sources PATH] [--build-directory PATH]
             [--include-directory PATH] [--install-directory PATH]
             [--build-package PACKAGE] [--skip-final-phase [SKIP_FINAL_PHASE]]
             [--build-script PATH] [--prepare-script PATH]
             [--postinst-script PATH] [--finalize-script PATH]
             [--source-file-transfer {copy-all,copy-git-cached,copy-git-others,copy-git-more,mount,None}]
             [--source-file-transfer-final {copy-all,copy-git-cached,copy-git-others,copy-git-more,mount,None}]
             [--source-resolve-symlinks [SOURCE_RESOLVE_SYMLINKS]]
             [--source-resolve-symlinks-final [SOURCE_RESOLVE_SYMLINKS_FINAL]]
             [--with-network [WITH_NETWORK]] [--settings PATH]
             [--base-image IMAGE] [--root-size BYTES] [--esp-size BYTES]
             [--xbootldr-size BYTES] [--swap-size BYTES] [--home-size BYTES]
             [--srv-size BYTES] [--var-size BYTES] [--tmp-size BYTES]
             [--usr-only [USR_ONLY]] [--checksum [CHECKSUM]] [--sign [SIGN]]
             [--key KEY] [--bmap [BMAP]]
             [--extra-search-path EXTRA_SEARCH_PATHS]
             [--qemu-headless [QEMU_HEADLESS]] [--qemu-smp SMP]
             [--qemu-mem MEM] [--network-veth [NETWORK_VETH]]
             [--ephemeral [EPHEMERAL]] [--ssh [SSH]] [--ssh-key PATH]
             [--ssh-timeout SECONDS] [--ssh-agent PATH] [--ssh-port PORT]
             [-C PATH] [--default PATH] [-a] [--all-directory PATH]
             [-B [AUTO_BUMP]]
             [--debug {run,build-script,workspace-command,disk}]
             {build,clean,help,summary,genkey,bump,serve,build,shell,boot,qemu,ssh}
             ...
mkosi: error: argument --manifest-format: unknown Format: 'ManifestFormat.json'
______________________ test_def_2[MkosiConfigManyParams] _______________________

cls = <enum 'ManifestFormat'>, name = 'ManifestFormat.json'

    @classmethod
    def from_string(cls: Any, name: str) -> Any:
        """A convenience method to be used with argparse"""
        try:
>           return cls[name]

mkosi/backend.py:97: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <enum 'ManifestFormat'>, name = 'ManifestFormat.json'

    def __getitem__(cls, name):
        """
        Return the member matching `name`.
        """
>       return cls._member_map_[name]
E       KeyError: 'ManifestFormat.json'

/usr/lib64/python3.11/enum.py:818: KeyError

During handling of the above exception, another exception occurred:

self = ArgumentParserMkosi(prog='mkosi', usage=None, description='Build Bespoke OS Images', formatter_class=<class 'mkosi.CustomHelpFormatter'>, conflict_handler='error', add_help=False)
action = CommaDelimitedListAction(option_strings=['--manifest-format'], dest='manifest_format', nargs=None, const=None, default...ype=<bound method Parseable.parse_list of <enum 'ManifestFormat'>>, choices=None, help='Manifest Format', metavar=None)
arg_string = 'ManifestFormat.json'

    def _get_value(self, action, arg_string):
        type_func = self._registry_get('type', action.type, action.type)
        if not callable(type_func):
            msg = _('%r is not callable')
            raise ArgumentError(action, msg % type_func)
    
        # convert the value to the appropriate type
        try:
>           result = type_func(arg_string)

/usr/lib64/python3.11/argparse.py:2500: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <enum 'ManifestFormat'>, string = 'ManifestFormat.json'

    @classmethod
    def parse_list(cls: Any, string: str) -> List[Any]:
>       return [cls.from_string(p) for p in string.split(",") if p]

mkosi/backend.py:103: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

.0 = <list_iterator object at 0x7f116ff7fbe0>

>   return [cls.from_string(p) for p in string.split(",") if p]

mkosi/backend.py:103: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <enum 'ManifestFormat'>, name = 'ManifestFormat.json'

    @classmethod
    def from_string(cls: Any, name: str) -> Any:
        """A convenience method to be used with argparse"""
        try:
            return cls[name]
        except KeyError:
>           raise argparse.ArgumentTypeError(f"unknown Format: {name!r}")
E           argparse.ArgumentTypeError: unknown Format: 'ManifestFormat.json'

mkosi/backend.py:99: ArgumentTypeError

During handling of the above exception, another exception occurred:

self = ArgumentParserMkosi(prog='mkosi', usage=None, description='Build Bespoke OS Images', formatter_class=<class 'mkosi.CustomHelpFormatter'>, conflict_handler='error', add_help=False)
args = ['@/tmp/pytest-of-mockbuild/pytest-0/test_def_2_MkosiConfigManyPara0/mkosi.default', '@mkosi.default.d/002_mkosi.conf', '--', 'build']
namespace = <[AttributeError("'int' object has no attribute 'name'") raised in repr()] Namespace object at 0x7f116fdf68d0>

    def parse_known_args(self, args=None, namespace=None):
        if args is None:
            # args default to the system args
            args = _sys.argv[1:]
        else:
            # make sure that args are mutable
            args = list(args)
    
        # default Namespace built from parser defaults
        if namespace is None:
            namespace = Namespace()
    
        # add any action defaults that aren't present
        for action in self._actions:
            if action.dest is not SUPPRESS:
                if not hasattr(namespace, action.dest):
                    if action.default is not SUPPRESS:
                        setattr(namespace, action.dest, action.default)
    
        # add any parser defaults that aren't present
        for dest in self._defaults:
            if not hasattr(namespace, dest):
                setattr(namespace, dest, self._defaults[dest])
    
        # parse the arguments and exit if there are any errors
        if self.exit_on_error:
            try:
>               namespace, args = self._parse_known_args(args, namespace)

/usr/lib64/python3.11/argparse.py:1877: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = ArgumentParserMkosi(prog='mkosi', usage=None, description='Build Bespoke OS Images', formatter_class=<class 'mkosi.CustomHelpFormatter'>, conflict_handler='error', add_help=False)
arg_strings = ['--distribution', 'fedora', '--release', '28', '--repositories', 'http://fedora/repos', ...]
namespace = <[AttributeError("'int' object has no attribute 'name'") raised in repr()] Namespace object at 0x7f116fdf68d0>

    def _parse_known_args(self, arg_strings, namespace):
        # replace arg strings that are file references
        if self.fromfile_prefix_chars is not None:
            arg_strings = self._read_args_from_files(arg_strings)
    
        # map all mutually exclusive arguments to the other arguments
        # they can't occur with
        action_conflicts = {}
        for mutex_group in self._mutually_exclusive_groups:
            group_actions = mutex_group._group_actions
            for i, mutex_action in enumerate(mutex_group._group_actions):
                conflicts = action_conflicts.setdefault(mutex_action, [])
                conflicts.extend(group_actions[:i])
                conflicts.extend(group_actions[i + 1:])
    
        # find all option indices, and determine the arg_string_pattern
        # which has an 'O' if there is an option at an index,
        # an 'A' if there is an argument, or a '-' if there is a '--'
        option_string_indices = {}
        arg_string_pattern_parts = []
        arg_strings_iter = iter(arg_strings)
        for i, arg_string in enumerate(arg_strings_iter):
    
            # all args after -- are non-options
            if arg_string == '--':
                arg_string_pattern_parts.append('-')
                for arg_string in arg_strings_iter:
                    arg_string_pattern_parts.append('A')
    
            # otherwise, add the arg to the arg strings
            # and note the index if it was an option
            else:
                option_tuple = self._parse_optional(arg_string)
                if option_tuple is None:
                    pattern = 'A'
                else:
                    option_string_indices[i] = option_tuple
                    pattern = 'O'
                arg_string_pattern_parts.append(pattern)
    
        # join the pieces together to form the pattern
        arg_strings_pattern = ''.join(arg_string_pattern_parts)
    
        # converts arg strings to the appropriate and then takes the action
        seen_actions = set()
        seen_non_default_actions = set()
    
        def take_action(action, argument_strings, option_string=None):
            seen_actions.add(action)
            argument_values = self._get_values(action, argument_strings)
    
            # error if this argument is not allowed with other previously
            # seen arguments, assuming that actions that use the default
            # value don't really count as "present"
            if argument_values is not action.default:
                seen_non_default_actions.add(action)
                for conflict_action in action_conflicts.get(action, []):
                    if conflict_action in seen_non_default_actions:
                        msg = _('not allowed with argument %s')
                        action_name = _get_action_name(conflict_action)
                        raise ArgumentError(action, msg % action_name)
    
            # take the action if we didn't receive a SUPPRESS value
            # (e.g. from a default)
            if argument_values is not SUPPRESS:
                action(self, namespace, argument_values, option_string)
    
        # function to convert arg_strings into an optional action
        def consume_optional(start_index):
    
            # get the optional identified at this index
            option_tuple = option_string_indices[start_index]
            action, option_string, explicit_arg = option_tuple
    
            # identify additional optionals in the same arg string
            # (e.g. -xyz is the same as -x -y -z if no args are required)
            match_argument = self._match_argument
            action_tuples = []
            while True:
    
                # if we found no optional action, skip it
                if action is None:
                    extras.append(arg_strings[start_index])
                    return start_index + 1
    
                # if there is an explicit argument, try to match the
                # optional's string arguments to only this
                if explicit_arg is not None:
                    arg_count = match_argument(action, 'A')
    
                    # if the action is a single-dash option and takes no
                    # arguments, try to parse more single-dash options out
                    # of the tail of the option string
                    chars = self.prefix_chars
                    if arg_count == 0 and option_string[1] not in chars:
                        action_tuples.append((action, [], option_string))

https://docs.python.org/3.11/whatsnew/3.11.html

For the build logs, see:
https://copr-be.cloud.fedoraproject.org/results/@python/python3.11/fedora-rawhide-x86_64/03525480-mkosi/

For all our attempts to build mkosi with Python 3.11, see:
https://copr.fedorainfracloud.org/coprs/g/python/python3.11/package/mkosi/

Testing and mass rebuild of packages is happening in copr. You can follow these instructions to test locally in mock if your package builds with Python 3.11:
https://copr.fedorainfracloud.org/coprs/g/python/python3.11/

Let us know here if you have any questions.

Python 3.11 is planned to be included in Fedora 37. To make that update smoother, we're building Fedora packages with all pre-releases of Python 3.11.
A build failure prevents us from testing all dependent packages (transitive [Build]Requires), so if this package is required a lot, it's important for us to get it fixed soon.
We'd appreciate help from the people who know this package best, but if you don't want to work on this now, let us know so we can try to work around it on our side.

Comment 1 Zbigniew Jędrzejewski-Szmek 2022-04-24 11:29:47 UTC
I pushed a fix to rawhide, but didn't build. I hope it'll get picked up automatically in the copr.

Comment 2 Miro Hrončok 2022-04-24 13:43:46 UTC
It did. Thanks.


Note You need to log in before you can comment on or make changes to this bug.