From: Mark Wooding Date: Sat, 11 Apr 2020 21:47:55 +0000 (+0100) Subject: @@@ mdwopt test and fix X-Git-Url: https://git.distorted.org.uk/~mdw/mLib-python/commitdiff_plain/d33275b6c806dd44debdb212294a3b722760ddca @@@ mdwopt test and fix --- diff --git a/TODO.org b/TODO.org index 1f1402f..8c43295 100644 --- a/TODO.org +++ b/TODO.org @@ -56,8 +56,8 @@ * [0/1] =trace= + [ ] =trace/trace.h= :: -* [2/3] =ui= - + [ ] =ui/mdwopt.h= :: interface needs thinking about +* [3/3] =ui= + + [X] =ui/mdwopt.h= :: + [X] =ui/quis.h= :: + [X] =ui/report.h= :: diff --git a/t/t-ui.py b/t/t-ui.py index 4d3a6e7..7b2dc89 100644 --- a/t/t-ui.py +++ b/t/t-ui.py @@ -65,9 +65,50 @@ class TestUI (U.TestCase): else: raise AssertionError("die didn't exit") - def test_mdwopt(me): - mo = M.MdwOpt() - print(list(mo)) +###-------------------------------------------------------------------------- +class TestMdwOpt (U.TestCase): + + def test_argv_default(me): + old_argv = SYS.argv + try: + SYS.argv = ["just", "another", "Python", "hacker"] + mo = M.MdwOpt() + me.assertEqual(mo.argv, SYS.argv) + finally: + SYS.argv = old_argv + + def test_basic(me): + mo = M.MdwOpt(argv = ["example", + "one", + "-abcarg", + "--opt", + "+de", + "two", + "--no-thing", + "-f", "alpha", + "--foo", "beta", + "--switch", "--frob", "--toggle", "--no-frob", + "three"], + shortopt = "abc:d+e+f:", + longopt = [("opt", "o"), + ("thing", "t", M.OPTF_NEGATE), + ("foo", None, M.OPTF_ARGREQ, "foo"), + ("switch", 1, M.OPTF_SWITCH, "flags"), + ("frob", 2, M.OPTF_SWITCH | M.OPTF_NEGATE, + "flags"), + ("toggle", 4, M.OPTF_SWITCH, "flags")], + flags = M.OPTF_NEGATION) + me.assertEqual(list(mo), [("a", None, 0), + ("b", None, 0), + ("c", "arg", 0), + ("o", None, 0), + ("d", None, M.OPTF_NEGATED), + ("e", None, M.OPTF_NEGATED), + ("t", None, M.OPTF_NEGATED), + ("f", "alpha", 0)]) + me.assertEqual(mo.argv[mo.ind:], ["one", "two", "three"]) + me.assertEqual(mo.state.foo, "beta") + me.assertEqual(mo.state.flags, 5) ###----- That's all, folks -------------------------------------------------- diff --git a/ui.c b/ui.c index 2912723..e50637e 100644 --- a/ui.c +++ b/ui.c @@ -89,6 +89,97 @@ end: /*----- Option parser -----------------------------------------------------*/ +typedef struct { + PyObject_HEAD + PyObject *attrs; +} optstate_pyobj; +static PyTypeObject *optstate_pytype; + +static PyObject *optstate_pywrap(PyTypeObject *ty) +{ + optstate_pyobj *me = 0; + + me = (optstate_pyobj *)ty->tp_alloc(ty, 0); if (!me) goto fail; + me->attrs = PyDict_New(); if (!me->attrs) goto fail; + return ((PyObject *)me); + +fail: + if (me) { + Py_XDECREF(me->attrs); + Py_DECREF(me); + } + return (0); +} + +static PyObject *optstate_pynew(PyTypeObject *ty, + PyObject *arg, PyObject *kw) +{ + static const char *const kwlist[] = { 0 }; + PyObject *me = 0; + + if (!PyArg_ParseTupleAndKeywords(arg, kw, ":new", KWLIST)) goto end; + me = optstate_pywrap(ty); +end: + return (me); +} + +static void optstate_pydealloc(PyObject *me) +{ + optstate_pyobj *st = (optstate_pyobj *)me; + Py_DECREF(st->attrs); + FREEOBJ(me); +} + +static const PyTypeObject optstate_pytype_skel = { + PyVarObject_HEAD_INIT(0, 0) /* Header */ + "OptState", /* @tp_name@ */ + sizeof(optstate_pyobj), /* @tp_basicsize@ */ + 0, /* @tp_itemsize@ */ + + optstate_pydealloc, /* @tp_dealloc@ */ + 0, /* @tp_print@ */ + 0, /* @tp_getattr@ */ + 0, /* @tp_setattr@ */ + 0, /* @tp_compare@ */ + 0, /* @tp_repr@ */ + 0, /* @tp_as_number@ */ + 0, /* @tp_as_sequence@ */ + 0, /* @tp_as_mapping@ */ + 0, /* @tp_hash@ */ + 0, /* @tp_call@ */ + 0, /* @tp_str@ */ + 0, /* @tp_getattro@ */ + 0, /* @tp_setattro@ */ + 0, /* @tp_as_buffer@ */ + Py_TPFLAGS_DEFAULT | /* @tp_flags@ */ + Py_TPFLAGS_BASETYPE, + + /* @tp_doc@ */ + "OptState()\n" + "\n" + "A passive and generic container for arbitrary object attributes.", + + 0, /* @tp_traverse@ */ + 0, /* @tp_clear@ */ + 0, /* @tp_richcompare@ */ + 0, /* @tp_weaklistoffset@ */ + 0, /* @tp_iter@ */ + 0, /* @tp_iternext@ */ + 0, /* @tp_methods@ */ + 0, /* @tp_members@ */ + 0, /* @tp_getset@ */ + 0, /* @tp_base@ */ + 0, /* @tp_dict@ */ + 0, /* @tp_descr_get@ */ + 0, /* @tp_descr_set@ */ + offsetof(optstate_pyobj, attrs), /* @tp_dictoffset@ */ + 0, /* @tp_init@ */ + PyType_GenericAlloc, /* @tp_alloc@ */ + optstate_pynew, /* @tp_new@ */ + 0, /* @tp_free@ */ + 0 /* @tp_is_gc@ */ +}; + struct optextra { PyObject *tag; PyObject *attr; @@ -111,7 +202,7 @@ static PyTypeObject *mdwopt_pytype; #define MDWOPT_OPT(o) (&((mdwopt_pyobj *)(o))->opt) #define MDWOPT_ARGV(o) (((mdwopt_pyobj *)(o))->argv) #define MDWOPT_SHORT(o) (((mdwopt_pyobj *)(o))->stringdata) -#define MDWOPT_LONG(o) (((mdwopt_pyobj *)(o))->longopt) +#define MDWOPT_LONG(o) stativ(((mdwopt_pyobj *)(o))->longopt) #define IXTAG(ix) (((ix)&0xff) | (((ix)&~0xff) << 2) | 0x200) #define TAGIX(tag) (((tag)&0xff) | (((tag)&~0x3ff) >> 2)) @@ -136,9 +227,9 @@ struct optbuild { opt_v opt; /* options */ extra_v extra; /* option extra data */ }; -#define OPTBUILD_INIT { DSTR_INIT, 0, DA_INIT, DA_INIT } +#define OPTBUILD_INIT { DSTR_INIT, 0, DA_INIT, DA_INIT, DA_INIT } -static PyObject *mdwopt_pynew(PyTypeObject *cls, PyObject *arg, PyObject *kw) +static PyObject *mdwopt_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw) { PyObject *argvobj = 0, *longoptobj = 0; PyObject *it = 0, *t = 0, *u = 0; @@ -233,8 +324,8 @@ static PyObject *mdwopt_pynew(PyTypeObject *cls, PyObject *arg, PyObject *kw) if (n < 3) opt->has_arg = 0; else { - u = PySequence_GetItem(t, 0); if (!u) goto end; - if (convint(u, &opt->has_arg)) goto end; + u = PySequence_GetItem(t, 2); if (!u) goto end; + if (!convint(u, &opt->has_arg)) goto end; Py_DECREF(u); u = 0; } @@ -254,11 +345,10 @@ static PyObject *mdwopt_pynew(PyTypeObject *cls, PyObject *arg, PyObject *kw) } /* Allocate the state value. */ - t = PyBaseObject_Type.tp_alloc(&PyBaseObject_Type, 0); - if (!t) goto end; + t = optstate_pywrap(optstate_pytype); if (!t) goto end; /* Allocate our return value. */ - me = (mdwopt_pyobj *)cls->tp_alloc(cls, 0); + me = (mdwopt_pyobj *)ty->tp_alloc(ty, 0); me->state = t; t = 0; me->flags = flags; me->narg = build.narg; @@ -273,15 +363,15 @@ static PyObject *mdwopt_pynew(PyTypeObject *cls, PyObject *arg, PyObject *kw) COPYTAB(extra, DA(&build.extra), DA_LEN(&build.extra)); /* Fill in the `argv' vector. */ - me->argv = xmalloc(build.narg*sizeof(*me->argv)); + me->argv = xmalloc((build.narg + 1)*sizeof(*me->argv)); for (i = 0; i < build.narg; i++) - me->argv[i] = me->stringdata + DA(&build.off)[i + 1]; + me->argv[i] = me->stringdata + DA(&build.off)[i]; me->argv[build.narg] = 0; /* Fix up the string pointers and values in the long-options table. */ for (i = 0; i < me->nlong; i++) { me->longopt[i].name = - me->stringdata + DA(&build.off)[i + build.narg + 1]; + me->stringdata + DA(&build.off)[i + build.narg]; me->longopt[i].val = IXTAG(i); } @@ -350,30 +440,34 @@ again: } else { if (m->extra[ix].attr == Py_None) { val = m->extra[ix].tag; Py_INCREF(val); } - else if (m->longopt[ix].has_arg&OPTF_SWITCH) { - t = PyObject_GetAttr(m->state, m->extra[ix].attr); - if (!t && PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyErr_Clear(); - t = PyInt_FromLong(0); if (!t) goto end; + else { + if (m->longopt[ix].has_arg&OPTF_SWITCH) { + t = PyObject_GetAttr(m->state, m->extra[ix].attr); + if (!t && PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + t = PyInt_FromLong(0); if (!t) goto end; + } + if (!f) + { v = PyNumber_Or(t, m->extra[ix].tag); if (!v) goto end; } + else { + u = PyNumber_Invert(m->extra[ix].tag); if (!u) goto end; + v = PyNumber_And(t, u); if (!v) goto end; + } + if (PyObject_SetAttr(m->state, m->extra[ix].attr, v)) goto end; + Py_DECREF(t); t = 0; + Py_XDECREF(u); u = 0; + Py_DECREF(v); v = 0; + } else { + if (PyObject_SetAttr(m->state, m->extra[ix].attr, + f ? + Py_None : + m->longopt[ix].has_arg&OPTF_ARG ? + arg : m->extra[ix].tag)) + goto end; } - if (!f) - { v = PyNumber_Or(t, m->extra[ix].tag); if (!v) goto end; } - else { - u = PyNumber_Invert(m->extra[ix].tag); if (!u) goto end; - v = PyNumber_And(t, u); if (!v) goto end; - } - if (PyObject_SetAttr(m->state, m->extra[ix].attr, v)) goto end; - Py_DECREF(t); Py_XDECREF(u); Py_DECREF(v); - } else { - if (PyObject_SetAttr(m->state, m->extra[ix].attr, - f ? - Py_None : - m->longopt[ix].has_arg&OPTF_ARG ? - arg : m->extra[ix].tag)) - goto end; + Py_DECREF(arg); arg = 0; + goto again; } - Py_DECREF(arg); - goto again; } rc = Py_BuildValue("(OOi)", val, arg, f); @@ -570,6 +664,15 @@ static const PyTypeObject mdwopt_pytype_skel = { /*----- Main code ---------------------------------------------------------*/ +static const struct nameval consts[] = { + CONST(OPTF_NOARG), CONST(OPTF_ARGREQ), CONST(OPTF_ARGOPT), CONST(OPTF_ARG), + CONST(OPTF_SWITCH), CONST(OPTF_NEGATE), + CONST(OPTF_NOLONGS), CONST(OPTF_NOSHORTS), CONST(OPTF_NUMBERS), + CONST(OPTF_NEGATION), CONST(OPTF_ENVVAR), CONST(OPTF_NOPROGNAME), + CONST(OPTF_NEGNUMBER), + CONST(OPTF_NEGATED) +}; + static const PyMethodDef methods[] = { #define METHNAME(name) meth_##name METH (ego, "ego(PROG): set program name") @@ -581,13 +684,16 @@ static const PyMethodDef methods[] = { void ui_pyinit(void) { + INITTYPE(optstate, root); INITTYPE(mdwopt, root); addmethods(methods); } void ui_pyinsert(PyObject *mod) { + INSERT("OptState", optstate_pytype); INSERT("MdwOpt", mdwopt_pytype); + setconstants(mod, consts); } int ui_pyready(void) { return (set_program_name()); }