key.c, catacomb/__init__.py: Split key file I/O into Python and C pieces.
authorMark Wooding <mdw@distorted.org.uk>
Tue, 22 Oct 2019 11:35:40 +0000 (12:35 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Mon, 25 Nov 2019 17:43:08 +0000 (17:43 +0000)
The new Catacomb `key_mergeline' and `key_extractline' functions were
specifically added to interface with language-specific I/O systems which
couldn't easily present as a `FILE *'.  Expose these new functions, and
implement the old file-oriented methods in terms of the new bindings.

catacomb/__init__.py
key.c
t/t-key.py

index 508f1f8..bd8aa57 100644 (file)
@@ -655,10 +655,23 @@ _augment(KeySZSet, _tmp)
 ### Key data objects.
 
 class _tmp:
+  def merge(me, file, report = None):
+    """KF.merge(FILE, [report = <built-in-reporter>])"""
+    name = file.name
+    lno = 1
+    for line in file:
+      me.mergeline(name, lno, line, report)
+      lno += 1
+    return me
   def __repr__(me): return '%s(%r)' % (_clsname(me), me.name)
 _augment(KeyFile, _tmp)
 
 class _tmp:
+  def extract(me, file, filter = ''):
+    """KEY.extract(FILE, [filter = <any>])"""
+    line = me.extractline(filter)
+    file.write(line)
+    return me
   def __repr__(me): return '%s(%r)' % (_clsname(me), me.fulltag)
 _augment(Key, _tmp)
 
diff --git a/key.c b/key.c
index 50c5321..0709c6e 100644 (file)
--- a/key.c
+++ b/key.c
@@ -1331,27 +1331,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 = TEXT_STR(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,
@@ -1489,7 +1483,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 }
@@ -1686,27 +1680,20 @@ 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 = { Py_None, 0 };
-  char *name;
-  PyObject *x = 0;
-  FILE *fp = 0;
-  int rc;
-  static const char *const kwlist[] = { "file", "report", 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 != 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 = TEXT_STR(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)
@@ -1817,7 +1804,8 @@ 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")
index e81217e..c33ea72 100644 (file)
@@ -169,10 +169,14 @@ class TestKeyFile (U.TestCase):
     me.assertRaises(TypeError, kf.bytype, 12345)
     me.assertRaises(C.KeyError, kf.byid, 0x12345678)
 
+    me.assertRaises(C.KeyError, kf.mergeline, "nowhere", 2, "")
+
     ## The keyring should be readonly.
     me.assertRaises(C.KeyError, kf.newkey, 0x12345678, "fail")
     me.assertRaises(C.KeyError, setattr, k, "tag", "foo")
     me.assertRaises(C.KeyError, delattr, k, "tag")
+    me.assertRaises(C.KeyError, kf.mergeline, "notexist", 1,
+                    "22222222:test integer,public:32519164 forever forever -")
     me.assertRaises(C.KeyError, setattr, k, "data", C.KeyDataString("foo"))
 
   def test_keywrite(me):
@@ -204,6 +208,9 @@ class TestKeyFile (U.TestCase):
     del k.comment
     me.assertEqual(k.comment, None)
 
+    kf.mergeline("notexist", 1,
+                 "22222222:test integer,public:32519164 forever forever -")
+
 ###--------------------------------------------------------------------------
 
 def keydata_equalp(kd0, kd1):