mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-06 16:05:05 +02:00
Proper handling of disabled cli flags
This commit is contained in:
@@ -6,9 +6,6 @@ from collections.abc import Sequence
|
||||
|
||||
|
||||
class CLIOptions:
|
||||
# these are present here because the parser doesn't set them on macOS
|
||||
cls: str = ''
|
||||
name: str = ''
|
||||
def __repr__(self) -> str:
|
||||
return repr(vars(self))
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ typedef struct FlagSpec {
|
||||
typedef struct CLISpec {
|
||||
cli_hash value_map;
|
||||
alias_hash alias_map;
|
||||
flag_hash flag_map;
|
||||
flag_hash flag_map, disabled_map;
|
||||
char **argv; int argc; // leftover args
|
||||
char **original_argv; int original_argc; // original args
|
||||
const char* errmsg;
|
||||
@@ -274,6 +274,7 @@ alloc_cli_spec(CLISpec *spec) {
|
||||
vt_init(&spec->value_map);
|
||||
vt_init(&spec->alias_map);
|
||||
vt_init(&spec->flag_map);
|
||||
vt_init(&spec->disabled_map);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -284,6 +285,7 @@ dealloc_cli_spec(void *v) {
|
||||
vt_cleanup(&spec->value_map);
|
||||
vt_cleanup(&spec->alias_map);
|
||||
vt_cleanup(&spec->flag_map);
|
||||
vt_cleanup(&spec->disabled_map);
|
||||
}
|
||||
|
||||
#define RAII_CLISpec(name) __attribute__((cleanup(dealloc_cli_spec))) CLISpec name = {0}; alloc_cli_spec(&name)
|
||||
@@ -428,6 +430,31 @@ get_string_cli_val(CLISpec *spec, const char *name) {
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool
|
||||
clival_as_python(const CLIValue *v, PyObject *is_seen, const char *dest, PyObject *ans) {
|
||||
#define S(fv) { \
|
||||
RAII_PyObject(temp, Py_BuildValue("NO", fv, is_seen)); if (!temp) return false; \
|
||||
if (PyDict_SetItemString(ans, dest, temp) != 0) return false; \
|
||||
}
|
||||
switch (v->type) {
|
||||
case CLI_VALUE_BOOL: S(PyBool_FromLong((long)v->boolval)); break;
|
||||
case CLI_VALUE_STRING: if (v->strval) { S(PyUnicode_FromString(v->strval)); } else { S(Py_NewRef(Py_None)); } break;
|
||||
case CLI_VALUE_CHOICE: S(PyUnicode_FromString(v->strval)); break;
|
||||
case CLI_VALUE_INT: S(PyLong_FromLongLong(v->intval)); break;
|
||||
case CLI_VALUE_FLOAT: S(PyFloat_FromDouble(v->floatval)); break;
|
||||
case CLI_VALUE_LIST: {
|
||||
RAII_PyObject(l, PyList_New(v->listval.count)); if (!l) return false;
|
||||
for (size_t i = 0; i < v->listval.count; i++) {
|
||||
PyObject *x = PyUnicode_FromString(v->listval.items[i]); if (!x) return false;
|
||||
PyList_SET_ITEM(l, i, x);
|
||||
}
|
||||
S(Py_NewRef(l));
|
||||
} break;
|
||||
}
|
||||
#undef S
|
||||
return true;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
cli_parse_result_as_python(CLISpec *spec) {
|
||||
if (PyErr_Occurred()) return NULL;
|
||||
@@ -440,25 +467,12 @@ cli_parse_result_as_python(CLISpec *spec) {
|
||||
cli_hash_itr i = vt_get(&spec->value_map, flag->dest);
|
||||
PyObject *is_seen = vt_is_end(i) ? Py_False : Py_True;
|
||||
const CLIValue *v = is_seen == Py_True ? &i.data->val : &flag->defval;
|
||||
#define S(fv) { RAII_PyObject(temp, Py_BuildValue("NO", fv, is_seen)); if (!temp) return NULL; \
|
||||
if (PyDict_SetItemString(ans, flag->dest, temp) != 0) return NULL;}
|
||||
switch (v->type) {
|
||||
case CLI_VALUE_BOOL: S(PyBool_FromLong((long)v->boolval)); break;
|
||||
case CLI_VALUE_STRING: if (v->strval) { S(PyUnicode_FromString(v->strval)); } else { S(Py_NewRef(Py_None)); } break;
|
||||
case CLI_VALUE_CHOICE: S(PyUnicode_FromString(v->strval)); break;
|
||||
case CLI_VALUE_INT: S(PyLong_FromLongLong(v->intval)); break;
|
||||
case CLI_VALUE_FLOAT: S(PyFloat_FromDouble(v->floatval)); break;
|
||||
case CLI_VALUE_LIST: {
|
||||
RAII_PyObject(l, PyList_New(v->listval.count)); if (!l) return NULL;
|
||||
for (size_t i = 0; i < v->listval.count; i++) {
|
||||
PyObject *x = PyUnicode_FromString(v->listval.items[i]); if (!x) return NULL;
|
||||
PyList_SET_ITEM(l, i, x);
|
||||
}
|
||||
S(Py_NewRef(l));
|
||||
} break;
|
||||
}
|
||||
if (!clival_as_python(v, is_seen, flag->dest, ans)) return NULL;
|
||||
}
|
||||
flag_map_for_loop(&spec->disabled_map) {
|
||||
const FlagSpec *flag = &itr.data->val;
|
||||
if (!clival_as_python(&flag->defval, Py_False, flag->dest, ans)) return NULL;
|
||||
}
|
||||
#undef S
|
||||
RAII_PyObject(leftover_args, PyList_New(spec->argc)); if (!leftover_args) return NULL;
|
||||
for (int i = 0; i < spec->argc; i++) {
|
||||
PyObject *t = PyUnicode_FromString(spec->argv[i]);
|
||||
|
||||
@@ -259,8 +259,34 @@ def add_list_values(*values: str) -> Iterator[str]:
|
||||
yield f'\tflag.defval.listval.items[{n}] = {c_str(value)};'
|
||||
|
||||
|
||||
def generate_c_for_opt(name: str, defval: Any, opt: OptionDict) -> Iterator[str]:
|
||||
yield f'\tflag = (FlagSpec){{.dest={c_str(name)},}};'
|
||||
match opt['type']:
|
||||
case 'bool-set' | 'bool-reset':
|
||||
yield '\tflag.defval.type = CLI_VALUE_BOOL;'
|
||||
yield f'\tflag.defval.boolval = {"true" if defval else "false"};'
|
||||
case 'int':
|
||||
yield '\tflag.defval.type = CLI_VALUE_INT;'
|
||||
yield f'\tflag.defval.intval = {defval};'
|
||||
case 'float':
|
||||
yield '\tflag.defval.type = CLI_VALUE_FLOAT;'
|
||||
yield f'\tflag.defval.floatval = {defval};'
|
||||
case 'list':
|
||||
yield '\tflag.defval.type = CLI_VALUE_LIST;'
|
||||
if defval:
|
||||
yield from add_list_values(*defval)
|
||||
case 'choices':
|
||||
yield '\tflag.defval.type = CLI_VALUE_CHOICE;'
|
||||
yield f'\tflag.defval.strval = {c_str(defval)};'
|
||||
yield from add_list_values(*opt['choices'])
|
||||
case _:
|
||||
yield '\tflag.defval.type = CLI_VALUE_STRING;'
|
||||
yield f'\tflag.defval.strval = {"NULL" if defval is None else c_str(defval)};'
|
||||
|
||||
|
||||
def generate_c_parser_for(funcname: str, spec: str) -> Iterator[str]:
|
||||
names_map, _, defaults_map = get_option_maps(parse_option_spec(spec)[0])
|
||||
seq, disabled = parse_option_spec(spec)
|
||||
names_map, _, defaults_map = get_option_maps(seq)
|
||||
if 'help' not in names_map:
|
||||
names_map['help'] = {'type': 'bool-set', 'aliases': ('--help', '-h')} # type: ignore
|
||||
defaults_map['help'] = False
|
||||
@@ -273,30 +299,13 @@ def generate_c_parser_for(funcname: str, spec: str) -> Iterator[str]:
|
||||
for name, opt in names_map.items():
|
||||
for alias in opt['aliases']:
|
||||
yield f'\tif (vt_is_end(vt_insert(&spec->alias_map, {c_str(alias)}, {c_str(name)}))) OOM;'
|
||||
yield f'\tflag = (FlagSpec){{.dest={c_str(name)},}};'
|
||||
defval = defaults_map[name]
|
||||
match opt['type']:
|
||||
case 'bool-set' | 'bool-reset':
|
||||
yield '\tflag.defval.type = CLI_VALUE_BOOL;'
|
||||
yield f'\tflag.defval.boolval = {"true" if defval else "false"};'
|
||||
case 'int':
|
||||
yield '\tflag.defval.type = CLI_VALUE_INT;'
|
||||
yield f'\tflag.defval.intval = {defval};'
|
||||
case 'float':
|
||||
yield '\tflag.defval.type = CLI_VALUE_FLOAT;'
|
||||
yield f'\tflag.defval.floatval = {defval};'
|
||||
case 'list':
|
||||
yield '\tflag.defval.type = CLI_VALUE_LIST;'
|
||||
if defval:
|
||||
yield from add_list_values(*defval)
|
||||
case 'choices':
|
||||
yield '\tflag.defval.type = CLI_VALUE_CHOICE;'
|
||||
yield f'\tflag.defval.strval = {c_str(defval)};'
|
||||
yield from add_list_values(*opt['choices'])
|
||||
case _:
|
||||
yield '\tflag.defval.type = CLI_VALUE_STRING;'
|
||||
yield f'\tflag.defval.strval = {"NULL" if defval is None else c_str(defval)};'
|
||||
yield from generate_c_for_opt(name, defaults_map[name], opt)
|
||||
yield '\tif (vt_is_end(vt_insert(&spec->flag_map, flag.dest, flag))) OOM;'
|
||||
for d in disabled:
|
||||
if not isinstance(d, str):
|
||||
yield from generate_c_for_opt(d['dest'], defval_for_opt(d), d)
|
||||
yield '\tif (vt_is_end(vt_insert(&spec->disabled_map, flag.dest, flag))) OOM;'
|
||||
|
||||
yield '\tparse_cli_loop(spec, true, argc, argv);'
|
||||
yield '}'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user