Coverage for klayout_pex/util/argparse_helpers.py: 89%
37 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-17 19:08 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-17 19:08 +0000
1#! /usr/bin/env python3
2#
3# --------------------------------------------------------------------------------
4# SPDX-FileCopyrightText: 2024-2025 Martin Jan Köhler and Harald Pretl
5# Johannes Kepler University, Institute for Integrated Circuits.
6#
7# This file is part of KPEX
8# (see https://github.com/iic-jku/klayout-pex).
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation, either version 3 of the License, or
13# (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22# SPDX-License-Identifier: GPL-3.0-or-later
23# --------------------------------------------------------------------------------
24#
26import argparse
27from enum import Enum, StrEnum
28from typing import *
30from rich_argparse import RichHelpFormatter
33def _group_name(name: str) -> str:
34 """
35 Title-case only the group names argparse invents.
37 rich-argparse title-cases every group name, which mangles the acronyms this
38 project uses — PEX25D would render as "Pex25D", FasterCap as "Fastercap".
39 A title carrying an uppercase letter was written deliberately and is left
40 alone; argparse's own all-lowercase defaults ("positional arguments",
41 "options") still get capitalized.
42 """
43 return name if any(c.isupper() for c in name) else name.title()
46RichHelpFormatter.group_name_formatter = _group_name
49def render_enum_help(topic: str,
50 enum_cls: Type[Enum],
51 print_default: bool = True,
52 lowercase_strenum: bool = False) -> str:
53 def canonic_string(name: str, member: str) -> str:
54 if issubclass(enum_cls, StrEnum):
55 if name.lower() == 'default':
56 return 'default'
57 return member.lower() if lowercase_strenum else member
58 return name.lower()
59 if not hasattr(enum_cls, 'DEFAULT'):
60 print_default = False
61 case_list = [f"'{canonic_string(name, member)}'"
62 for name, member in enum_cls.__members__.items()
63 if name.lower() != 'default']
64 enum_help = f"{topic} ∈ \u007b{', '.join(case_list)}\u007d"
65 if print_default:
66 default_case: enum_cls = getattr(enum_cls, 'DEFAULT')
67 if issubclass(enum_cls, StrEnum):
68 default_value: str = default_case.value
69 if lowercase_strenum:
70 default_value = default_value.lower()
71 else:
72 default_value = default_case.name.lower()
73 enum_help += f".\nDefaults to '{default_value}'"
74 return enum_help
77def true_or_false(arg) -> bool:
78 if isinstance(arg, bool):
79 return arg
81 match str(arg).lower():
82 case 'yes' | 'true' | 't' | 'y' | 1:
83 return True
84 case 'no' | 'false' | 'f' | 'n' | 0:
85 return False
86 case _:
87 raise argparse.ArgumentTypeError('Boolean value expected.')