algorithms.c: Add bindings for STROBE.
[catacomb-python] / key.c
diff --git a/key.c b/key.c
index 5057b2f..82bbb19 100644 (file)
--- a/key.c
+++ b/key.c
@@ -51,7 +51,7 @@ static PyObject *kxmeth___init__(PyObject *me, PyObject *arg)
   if (PyObject_SetAttrString(me, "err", x)) goto end;
   Py_DECREF(x); x = 0;
 
-  x = PyString_FromString(key_strerror(err)); if (!x) goto end;
+  x = TEXT_FROMSTR(key_strerror(err)); if (!x) goto end;
   if (PyObject_SetAttrString(me, "errstring", x)) goto end;
   Py_DECREF(x); x = 0;
 
@@ -87,9 +87,9 @@ static PyObject *kxmeth___str__(PyObject *me, PyObject *arg)
   if (err >= 0 && err < N(tab)) errtag = tab[err];
   else errtag = "<unknown>";
   if ((x = PyObject_GetAttrString(me, "errstring")) == 0 ||
-      (errstr = PyString_AsString(x)) == 0)
+      (errstr = TEXT_STR(x)) == 0)
     goto done;
-  rc = PyString_FromFormat("%s (%ld): %s", errtag, -err, errstr);
+  rc = TEXT_FORMAT("%s (%ld): %s", errtag, -err, errstr);
 
 done:
   Py_XDECREF(x);
@@ -181,7 +181,7 @@ static int convfilter(PyObject *x, void *p)
   int err;
   int rc = 0;
 
-  if ((fs = PyString_AsString(x)) != 0) {
+  if ((fs = TEXT_STR(x)) != 0) {
     if ((err = key_readflags(fs, &end, &f->f, &f->m)) != 0)
       KEYERR(err);
     if (*end)
@@ -220,7 +220,7 @@ static int convflags(PyObject *x, void *p)
     return (1);
   else {
     PyErr_Clear();
-    if ((fs = PyString_AsString(x)) != 0) {
+    if ((fs = TEXT_STR(x)) != 0) {
       if ((err = key_readflags(fs, &end, f, 0)) != 0)
        KEYERR(err);
       if (*end)
@@ -261,7 +261,7 @@ static PyObject *kdmeth_writeflags(PyObject *me, PyObject *arg)
 
   if (!PyArg_ParseTuple(arg, "O&:key_writeflags", convuint, &f)) return (0);
   key_writeflags(f, &d);
-  rc = PyString_FromStringAndSize(d.buf, d.len);
+  rc = TEXT_FROMSTRLEN(d.buf, d.len);
   dstr_destroy(&d);
   return (rc);
 }
@@ -338,7 +338,7 @@ static PyObject *kdmeth_write(PyObject *me, PyObject *arg, PyObject *kw)
                                   convfilter, &f))
     return (0);
   key_write(KEYDATA_KD(me), &d, &f);
-  rc = PyString_FromStringAndSize(d.buf, d.len);
+  rc = TEXT_FROMSTRLEN(d.buf, d.len);
   dstr_destroy(&d);
   return (rc);
 }
@@ -377,14 +377,13 @@ end:
 
 static PyObject *kdmeth_lock(PyObject *me, PyObject *arg)
 {
-  char *p;
-  Py_ssize_t n;
+  struct bin pp;
   PyObject *rc = 0;
   key_data *kd;
 
-  if (!PyArg_ParseTuple(arg, "s#:lock", &p, &n))
+  if (!PyArg_ParseTuple(arg, "O&:lock", convbin, &pp))
     goto end;
-  key_lock(&kd, KEYDATA_KD(me), p, n);
+  key_lock(&kd, KEYDATA_KD(me), pp.p, pp.sz);
   rc = keydata_pywrap(kd);
 end:
   return (rc);
@@ -406,14 +405,13 @@ end:
 
 static PyObject *kdmeth_decode(PyObject *me, PyObject *arg)
 {
-  const char *p;
-  Py_ssize_t n;
+  struct bin in;
   key_data *kd;
   PyObject *rc = 0;
 
-  if (!PyArg_ParseTuple(arg, "s#:decode", &p, &n)) goto end;
-  if ((kd = key_decode(p, n)) == 0) KEYERR(KERR_MALFORMED);
-  rc = keydata_pywrap(kd);
+  if (!PyArg_ParseTuple(arg, "O&:decode", convbin, &in)) goto end;
+  if ((kd = key_decode(in.p, in.sz)) == 0) KEYERR(KERR_MALFORMED);
+  rc = keydata_pywrap(kd);
 end:
   return (rc);
 }
@@ -496,17 +494,16 @@ static const PyTypeObject keydata_pytype_skel = {
 static PyObject *keydatabin_pynew(PyTypeObject *ty,
                                  PyObject *arg, PyObject *kw)
 {
-  char *p;
-  Py_ssize_t n;
+  struct bin in;
   unsigned f = 0;
   keydata_pyobj *me = 0;
   static const char *const kwlist[] = { "key", "flags", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|O&:new", KWLIST,
-                                  &p, &n, convflags, &f))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new", KWLIST,
+                                  convbin, &in, convflags, &f))
     goto end;
   me = (keydata_pyobj *)ty->tp_alloc(ty, 0);
-  me->kd = key_newbinary(f & ~KF_ENCMASK, p, n);
+  me->kd = key_newbinary(f & ~KF_ENCMASK, in.p, in.sz);
 end:
   return ((PyObject *)me);
 }
@@ -573,17 +570,16 @@ static const PyTypeObject keydatabin_pytype_skel = {
 static PyObject *keydataenc_pynew(PyTypeObject *ty,
                                  PyObject *arg, PyObject *kw)
 {
-  char *p;
-  Py_ssize_t n;
+  struct bin in;
   unsigned f = 0;
   keydata_pyobj *me = 0;
   static const char *const kwlist[] = { "key", "flags", 0 };
 
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s#|O&:new", KWLIST,
-                                  &p, &n, convflags, &f))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O&:new", KWLIST,
+                                  convbin, &in, convflags, &f))
     goto end;
   me = (keydata_pyobj *)ty->tp_alloc(ty, 0);
-  me->kd = key_newencrypted(f & ~KF_ENCMASK, p, n);
+  me->kd = key_newencrypted(f & ~KF_ENCMASK, in.p, in.sz);
 end:
   return ((PyObject *)me);
 }
@@ -599,9 +595,8 @@ end:
 
 static PyObject *kdemeth_lock(PyObject *me, PyObject *arg)
 {
-  char *hunoz;
-  Py_ssize_t hukairz;
-  if (!PyArg_ParseTuple(arg, "s#:lock", &hunoz, &hukairz)) goto end;
+  struct bin hunoz;
+  if (!PyArg_ParseTuple(arg, "O&:lock", convbin, &hunoz)) goto end;
   KEYERR(KERR_WRONGTYPE);
 end:
   return (0);
@@ -625,15 +620,14 @@ end:
 
 static PyObject *kdemeth_unlock(PyObject *me, PyObject *arg)
 {
-  char *p;
-  Py_ssize_t n;
+  struct bin pp;
   int err;
   PyObject *rc = 0;
   key_data *kd;
 
-  if (!PyArg_ParseTuple(arg, "s#:unlock", &p, &n))
+  if (!PyArg_ParseTuple(arg, "O&:unlock", convbin, &pp))
     goto end;
-  if ((err = key_unlock(&kd, KEYDATA_KD(me), p, n)) != 0)
+  if ((err = key_unlock(&kd, KEYDATA_KD(me), pp.p, pp.sz)) != 0)
     KEYERR(err);
   rc = keydata_pywrap(kd);
 end:
@@ -801,7 +795,7 @@ end:
 }
 
 static PyObject *kdsget_str(PyObject *me, void *hunoz)
-  { return (PyString_FromString(KEYDATA_KD(me)->u.p)); }
+  { return (TEXT_FROMSTR(KEYDATA_KD(me)->u.p)); }
 
 static const PyGetSetDef keydatastr_pygetset[] = {
 #define GETSETNAME(op, name) kds##op##_##name
@@ -945,7 +939,7 @@ static void *keydatastruct_gmlookup(PyObject *me, PyObject *k, unsigned *f)
   key_struct *ks;
 
   assert((kd->e&KF_ENCMASK) == KENC_STRUCT);
-  if ((tag = PyString_AsString(k)) == 0) return (0);
+  if ((tag = TEXT_STR(k)) == 0) return (0);
   if (f) { key_split(&kd); KEYDATA_KD(me) = kd; }
   ks = sym_find(&kd->u.s, tag, -1, f ? sizeof(key_struct) : 0, f);
   if (ks && f && !*f) ks->k = 0;
@@ -964,7 +958,7 @@ static void *keydatastruct_gmiternext(PyObject *me, void *i)
   { return (sym_next(i)); }
 
 static PyObject *keydatastruct_gmentrykey(PyObject *me, void *e)
-  { key_struct *ks = e; return (PyString_FromString(SYM_NAME(ks))); }
+  { key_struct *ks = e; return (TEXT_FROMSTR(SYM_NAME(ks))); }
 
 static PyObject *keydatastruct_gmentryvalue(PyObject *me, void *e)
 {
@@ -1021,46 +1015,48 @@ static const gmap_ops keydatastruct_gmops = {
   keydatastruct_gmdelentry
 };
 
+static int populate_struct(key_data *kd, PyObject *map)
+{
+  PyObject *it = 0, *name = 0, *val = 0;
+  const char *p;
+  int rc = -1;
+
+  if (!PyMapping_Check(map)) TYERR("subkeys must be an iterable mapping");
+  if ((it = PyObject_GetIter(map)) == 0) goto end;
+  while ((name = PyIter_Next(it)) != 0) {
+    if ((p = TEXT_STR(name)) == 0 ||
+       (val = PyObject_GetItem(map, name)) == 0)
+      goto end;
+    if (!KEYDATA_PYCHECK(val))
+      TYERR("subkey objects must be instances of KeyData");
+    if (key_structfind(kd, p)) VALERR("duplicate tag");
+    key_structset(kd, p, KEYDATA_KD(val));
+    Py_DECREF(name); name = 0;
+    Py_DECREF(val); val = 0;
+  }
+  if (PyErr_Occurred()) goto end;
+  rc = 0;
+end:
+  Py_XDECREF(it); Py_XDECREF(name); Py_XDECREF(val);
+  return (rc);
+}
+
 static PyObject *keydatastruct_pynew(PyTypeObject *ty,
                                     PyObject *arg, PyObject *kw)
 {
   PyObject *sub = 0;
-  PyObject *it = 0, *name = 0, *val = 0;
-  char *p;
   keydata_pyobj *me = 0;
   key_data *kd = 0;
-  static const char *const kwlist[] = { "subkeys", 0 };
 
-  Py_XINCREF(arg); Py_XINCREF(kw);
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O:new", KWLIST, &sub))
-    goto end;
+  if (!PyArg_ParseTuple(arg, "|O:new", &sub)) goto end;
   kd = key_newstruct();
-  if (sub) {
-    if (!PyMapping_Check(sub))
-      TYERR("subkeys must be an iterable mapping");
-    if ((it = PyObject_GetIter(sub)) == 0)
-      goto end;
-    while ((name = PyIter_Next(it)) != 0) {
-      if ((p = PyString_AsString(name)) == 0 ||
-         (val = PyObject_GetItem(sub, name)) == 0)
-       goto end;
-      if (!KEYDATA_PYCHECK(val))
-       TYERR("subkey objects must be subclasses of KeyData");
-      key_structset(kd, p, KEYDATA_KD(val));
-      Py_DECREF(name); name = 0;
-      Py_DECREF(val); val = 0;
-    }
-    if (PyErr_Occurred())
-      goto end;
-    Py_DECREF(it); it = 0;
-  }
+  if (sub && populate_struct(kd, sub)) goto end;
+  if (kw && populate_struct(kd, kw)) goto end;
   me = (keydata_pyobj *)ty->tp_alloc(ty, 0);
   me->gmops = &keydatastruct_gmops;
-  me->kd = kd;
+  me->kd = kd; kd = 0;
 end:
-  if (kd && !me) key_drop(kd);
-  Py_XDECREF(name); Py_XDECREF(val); Py_XDECREF(it);
-  Py_XDECREF(arg); Py_XDECREF(kw);
+  if (kd) key_drop(kd);
   return ((PyObject *)me);
 }
 
@@ -1125,7 +1121,7 @@ static const PyTypeObject keydatastruct_pytype_skel = {
 
 static void *keyattrs_gmlookup(PyObject *me, PyObject *k, unsigned *f)
 {
-  char *name = PyString_AsString(k);
+  char *name = TEXT_STR(k);
   key_attr *a = 0;
 
   if (!name) goto end;
@@ -1143,10 +1139,10 @@ static void *keyattrs_gmiternext(PyObject *me, void *i)
   { return (sym_next(i)); }
 
 static PyObject *keyattrs_gmentrykey(PyObject *me, void *e)
-  { return (PyString_FromString(SYM_NAME(e))); }
+  { return (TEXT_FROMSTR(SYM_NAME(e))); }
 
 static PyObject *keyattrs_gmentryvalue(PyObject *me, void *e)
-  { return (PyString_FromString(((key_attr *)e)->p)); }
+  { return (TEXT_FROMSTR(((key_attr *)e)->p)); }
 
 static int keyattrs_gmsetentry(PyObject *me, void *e, PyObject *val)
 {
@@ -1155,8 +1151,8 @@ static int keyattrs_gmsetentry(PyObject *me, void *e, PyObject *val)
   Py_ssize_t n;
   int rc = -1;
 
-  if (!PyString_Check(val)) TYERR("expected string");
-  p = PyString_AS_STRING(val); n = PyString_GET_SIZE(val);
+  if (!TEXT_CHECK(val)) TYERR("expected string");
+  TEXT_PTRLEN(val, p, n);
   if (n > 255) VALERR("attribute too long");
   if (memchr(p, 0, n)) VALERR("attribute must not contain nul");
   if (a->p) xfree(a->p);
@@ -1305,6 +1301,21 @@ static void key_pydealloc(PyObject *me)
   FREEOBJ(me);
 }
 
+static Py_hash_t key_pyhash(PyObject *me)
+  { return ((Py_hash_t)KEY_K(me)); }
+
+static PyObject *key_pyrichcompare(PyObject *me, PyObject *you, int op)
+{
+  if (!KEY_PYCHECK(you)) RETURN_NOTIMPL;
+  switch (op) {
+    case Py_EQ: return (getbool(KEY_K(me) == KEY_K(you)));
+    case Py_NE: return (getbool(KEY_K(me) == KEY_K(you)));
+    default: TYERR("ordering makes no sense");
+  }
+end:
+  return (0);
+}
+
 static PyObject *kmeth_delete(PyObject *me)
 {
   int err;
@@ -1337,27 +1348,21 @@ end:
   return (0);
 }
 
-static PyObject *kmeth_extract(PyObject *me, PyObject *arg, PyObject *kw)
+static PyObject *kmeth_extractline(PyObject *me, PyObject *arg, PyObject *kw)
 {
   key_filter f = { 0, 0 };
-  PyObject *file;
-  PyObject *nameobj;
-  char *name;
-  FILE *fp;
-  static const char *const kwlist[] = { "file", "filter", 0 };
-
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!|O&:extract", KWLIST,
-                                  &PyFile_Type, &file,
-                                  convfilter, &f) ||
-      (fp = PyFile_AsFile(file)) == 0 ||
-      (nameobj = PyFile_Name(file)) == 0 ||
-      (name = PyString_AsString(nameobj)) == 0)
+  dstr d = DSTR_INIT;
+  PyObject *rc = 0;
+  static const char *const kwlist[] = { "filter", 0 };
+
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "|O&:extract", KWLIST,
+                                  convfilter, &f))
     goto end;
-  if (key_extract(KEY_KF(me), KEY_K(me), fp, &f))
-    OSERR(name);
-  RETURN_ME;
+  key_extractline(KEY_KF(me), KEY_K(me), &d, &f);
+  rc = TEXT_FROMSTRLEN(d.buf, d.len);
 end:
-  return (0);
+  dstr_destroy(&d);
+  return (rc);
 }
 
 static PyObject *kmeth_fingerprint(PyObject *me,
@@ -1378,7 +1383,7 @@ static PyObject *kget_id(PyObject *me, void *hunoz)
 static PyObject *kget_file(PyObject *me, void *hunoz)
   { RETURN_OBJ(KEY_KFOBJ(me)); }
 static PyObject *kget_type(PyObject *me, void *hunoz)
-  { return (PyString_FromString(KEY_K(me)->type)); }
+  { return (TEXT_FROMSTR(KEY_K(me)->type)); }
 static PyObject *kget_exptime(PyObject *me, void *hunoz)
   { return (getulong(KEY_K(me)->exp)); }
 static PyObject *kget_deltime(PyObject *me, void *hunoz)
@@ -1449,7 +1454,7 @@ static PyObject *kget_fulltag(PyObject *me, void *hunoz)
   PyObject *rc;
 
   key_fulltag(KEY_K(me), &d);
-  rc = PyString_FromStringAndSize(d.buf, d.len);
+  rc = TEXT_FROMSTRLEN(d.buf, d.len);
   dstr_destroy(&d);
   return (rc);
 }
@@ -1457,7 +1462,7 @@ static PyObject *kget_fulltag(PyObject *me, void *hunoz)
 static PyObject *kget_tag(PyObject *me, void *hunoz)
 {
   if (!KEY_K(me)->tag) RETURN_NONE;
-  return (PyString_FromString(KEY_K(me)->tag));
+  return (TEXT_FROMSTR(KEY_K(me)->tag));
 }
 static int kset_tag(PyObject *me, PyObject *x, void *hunoz)
 {
@@ -1465,7 +1470,7 @@ static int kset_tag(PyObject *me, PyObject *x, void *hunoz)
   char *tag;
 
   if (!x || x == Py_None) tag = 0;
-  else if ((tag = PyString_AsString(x)) == 0) goto end;
+  else if ((tag = TEXT_STR(x)) == 0) goto end;
   if ((err = key_settag(KEY_KF(me), KEY_K(me), tag)) != 0) KEYERR(err);
   return (0);
 end:
@@ -1475,7 +1480,7 @@ end:
 static PyObject *kget_comment(PyObject *me, void *hunoz)
 {
   if (!KEY_K(me)->c) RETURN_NONE;
-  return (PyString_FromString(KEY_K(me)->c));
+  return (TEXT_FROMSTR(KEY_K(me)->c));
 }
 static int kset_comment(PyObject *me, PyObject *x, void *hunoz)
 {
@@ -1483,7 +1488,7 @@ static int kset_comment(PyObject *me, PyObject *x, void *hunoz)
   char *c;
 
   if (!x || x == Py_None) c = 0;
-  else if ((c = PyString_AsString(x)) == 0) goto end;
+  else if ((c = TEXT_STR(x)) == 0) goto end;
   if ((err = key_setcomment(KEY_KF(me), KEY_K(me), c)) != 0) KEYERR(err);
   return (0);
 end:
@@ -1495,7 +1500,7 @@ static const PyMethodDef key_pymethods[] = {
   NAMETH(delete,       "KEY.delete()")
   NAMETH(expire,       "KEY.expire()")
   METH (used,          "KEY.used(TIME)")
-  KWMETH(extract,      "KEY.extract(FILE, [filter = <any>])")
+  KWMETH(extractline,  "KEY.extractline([filter = <any>])")
   KWMETH(fingerprint,  "KEY.fingerprint(HASH, [filter = '-secret'])")
 #undef METHNAME
   { 0 }
@@ -1533,7 +1538,7 @@ static const PyTypeObject key_pytype_skel = {
   0,                                   /* @tp_as_number@ */
   0,                                   /* @tp_as_sequence@ */
   0,                                   /* @tp_as_mapping@ */
-  0,                                   /* @tp_hash@ */
+  key_pyhash,                          /* @tp_hash@ */
   0,                                   /* @tp_call@ */
   0,                                   /* @tp_str@ */
   0,                                   /* @tp_getattro@ */
@@ -1547,7 +1552,7 @@ static const PyTypeObject key_pytype_skel = {
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
-  0,                                   /* @tp_richcompare@ */
+  key_pyrichcompare,                   /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
   0,                                   /* @tp_iternext@ */
@@ -1578,7 +1583,7 @@ static key *bytag(PyObject *me, PyObject *tagobj)
     k = key_byid(KEYFILE_KF(me), id);
   else {
     PyErr_Clear();
-    if ((tag = PyString_AsString(tagobj)) == 0)
+    if ((tag = TEXT_STR(tagobj)) == 0)
       goto end;
     k = key_bytag(KEYFILE_KF(me), tag);
   }
@@ -1625,7 +1630,7 @@ static void pythonreporter(const char *file, int line,
 
   if (ri->stop)
     return;
-  if (!ri->func)
+  if (ri->func == Py_None)
     key_moan(file, line, msg, 0);
   else if ((res = PyObject_CallFunction(ri->func, "sis",
                                        file, line, msg)) == 0)
@@ -1637,17 +1642,16 @@ static void pythonreporter(const char *file, int line,
 static PyObject *keyfile_pynew(PyTypeObject *ty,
                               PyObject *arg, PyObject *kw)
 {
-  struct reportinfo ri = { 0, 0 };
+  struct reportinfo ri = { Py_None, 0 };
   char *file = 0;
   unsigned how = KOPEN_READ;
   keyfile_pyobj *rc = 0;
   static const char *const kwlist[] = { "file", "how", "report", 0 };
 
-  Py_XINCREF(arg); Py_XINCREF(kw);
   if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|iO:new", KWLIST,
                                   &file, &how, &ri.func))
     goto end;
-  if (ri.func && !PyCallable_Check(ri.func))
+  if (ri.func != Py_None && !PyCallable_Check(ri.func))
     TYERR("reporter function not callable");
   if ((rc = (keyfile_pyobj *)ty->tp_alloc(ty, 0)) == 0)
     goto end;
@@ -1666,7 +1670,6 @@ end:
     rc = 0;
   }
 done:
-  Py_XDECREF(arg); Py_XDECREF(kw);
   return ((PyObject *)rc);
 }
 
@@ -1692,73 +1695,81 @@ end:
   return (0);
 }
 
-static PyObject *kfmeth_merge(PyObject *me, PyObject *arg, PyObject *kw)
+static PyObject *kfmeth_mergeline(PyObject *me, PyObject *arg, PyObject *kw)
 {
-  struct reportinfo ri = { 0, 0 };
-  char *name;
-  PyObject *x = 0;
-  FILE *fp = 0;
-  int rc;
-  static const char *const kwlist[] = { "file", "report", 0 };
+  struct reportinfo ri = { Py_None, 0 };
+  const char *file, *line;
+  int lno, rc;
+  static const char *const kwlist[] = { "name", "lno", "line", "report", 0 };
 
-  Py_XINCREF(arg); Py_XINCREF(kw);
-  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O!|O:merge", KWLIST,
-                                  &PyFile_Type, &x, &ri.func))
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "sis|O:merge", KWLIST,
+                                  &file, &lno, &line, &ri.func))
     goto end;
-  if (ri.func && !PyCallable_Check(ri.func))
+  if (ri.func != Py_None && !PyCallable_Check(ri.func))
     TYERR("reporter function not callable");
-  if ((fp = PyFile_AsFile(x)) == 0)
-    goto end;
-  x = PyFile_Name(x);
-  if ((name = PyString_AsString(x)) == 0)
-    goto end;
-  rc = key_merge(KEYFILE_KF(me), name, fp, pythonreporter, &ri);
+  rc = key_mergeline(KEYFILE_KF(me), file, lno, line, pythonreporter, &ri);
   if (ri.stop)
     goto end;
   if (rc != 0)
     KEYERR(rc);
-  Py_XDECREF(arg); Py_XDECREF(kw);
   RETURN_ME;
 
 end:
-  Py_XDECREF(arg); Py_XDECREF(kw);
   return (0);
 }
 
-static PyObject *kfmeth_byid(PyObject *me, PyObject *arg)
+static PyObject *kfmeth_byid(PyObject *me, PyObject *arg, PyObject *kw)
 {
   uint32 id;
   key *k;
+  PyObject *failp = Py_True;
   PyObject *rc = 0;
+  static const char *const kwlist[] = { "id", "fail", 0 };
 
-  if (!PyArg_ParseTuple(arg, "O&:byid", convu32, &id)) goto end;
-  if ((k = key_byid(KEYFILE_KF(me), id)) == 0) KEYERR(KERR_NOTFOUND);
-  rc = key_pywrap(me, k);
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O&|O:byid", KWLIST,
+                                  convu32, &id, &failp))
+    goto end;
+  if ((k = key_byid(KEYFILE_KF(me), id)) != 0) rc = key_pywrap(me, k);
+  else if (PyObject_IsTrue(failp)) KEYERR(KERR_NOTFOUND);
+  else RETURN_NONE;
 end:
   return (rc);
 }
 
-static PyObject *kfmeth_bytype(PyObject *me, PyObject *arg)
+static PyObject *kfmeth_bytype(PyObject *me, PyObject *arg, PyObject *kw)
 {
   char *type;
   key *k;
+  PyObject *failp = Py_True;
   PyObject *rc = 0;
+  static const char *const kwlist[] = { "type", "fail", 0 };
 
-  if (!PyArg_ParseTuple(arg, "s:bytype", &type)) goto end;
-  if ((k = key_bytype(KEYFILE_KF(me), type)) == 0) RETURN_NONE;
-  rc = key_pywrap(me, k);
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "s|O:bytype", KWLIST,
+                                  &type, &failp))
+    goto end;
+  if ((k = key_bytype(KEYFILE_KF(me), type)) != 0) rc = key_pywrap(me, k);
+  else if (PyObject_IsTrue(failp)) KEYERR(KERR_NOTFOUND);
+  else RETURN_NONE;
 end:
   return (rc);
 }
 
-static PyObject *kfmeth_bytag(PyObject *me, PyObject *arg)
+static PyObject *kfmeth_bytag(PyObject *me, PyObject *arg, PyObject *kw)
 {
   PyObject *tagobj;
   key *k;
+  PyObject *failp = Py_True;
+  PyObject *rc = 0;
+  static const char *const kwlist[] = { "type", "fail", 0 };
 
-  if (!PyArg_ParseTuple(arg, "O:bytag", &tagobj)) return (0);
-  if ((k = bytag(me, tagobj)) == 0) RETURN_NONE;
-  else return (key_pywrap(me, k));
+  if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:bytag", KWLIST,
+                                  &tagobj, &failp))
+    goto end;
+  if ((k = bytag(me, tagobj)) != 0) rc = key_pywrap(me, k);
+  else if (PyObject_IsTrue(failp)) KEYERR(KERR_NOTFOUND);
+  else RETURN_NONE;
+end:
+  return (rc);
 }
 
 static PyObject *kfmeth_newkey(PyObject *me, PyObject *arg, PyObject *kw)
@@ -1812,7 +1823,7 @@ end:
 }
 
 static PyObject *kfget_name(PyObject *me, void *hunoz)
-  { return (PyString_FromString(KEYFILE_KF(me)->name)); }
+  { return (TEXT_FROMSTR(KEYFILE_KF(me)->name)); }
 static PyObject *kfget_modifiedp(PyObject *me, void *hunoz)
   { return (getbool(KEYFILE_KF(me)->f & KF_MODIFIED)); }
 static PyObject *kfget_writep(PyObject *me, void *hunoz)
@@ -1823,12 +1834,13 @@ static PyObject *kfget_filep(PyObject *me, void *hunoz)
 static const PyMethodDef keyfile_pymethods[] = {
 #define METHNAME(func) kfmeth_##func
   NAMETH(save,         "KF.save()")
-  KWMETH(merge,                "KF.merge(FILE, [report = <built-in-reporter>])")
+  KWMETH(mergeline,    "KF.mergeline(NAME, LNO, LINE, "
+                                          "[report = <built-in-reporter>])")
   KWMETH(newkey,       "KF.newkey(ID, TYPE, [exptime = KEXP_FOREVER]) "
                                                                    "-> KEY")
-  METH (byid,          "KF.byid(KEYID) -> KEY|None")
-  METH (bytype,        "KF.bytype(TYPE) -> KEY|None")
-  METH (bytag,         "KF.bytag(TAG) -> KEY|None")
+  KWMETH(byid,         "KF.byid(KEYID, [fail = True]) -> KEY|None")
+  KWMETH(bytype,       "KF.bytype(TYPE, [fail = True]) -> KEY|None")
+  KWMETH(bytag,                "KF.bytag(TAG, [fail = True]) -> KEY|None")
   KWMETH(qtag,         "KF.qtag(TAG, [new = KD]) -> FULLTAG, KEY, OLDKD")
   GMAP_ROMETHODS
 #undef METHNAME