[SATYR PATCH 5/6] python: distance computation between pair of threads
Martin Milata
mmilata at redhat.com
Fri Jun 21 11:59:16 UTC 2013
The distance computation function is now exposed as a
BaseThread.distance(otherthread, [dist_type]) method.
Related to #63.
Signed-off-by: Martin Milata <mmilata at redhat.com>
---
include/distance.h | 5 ++++-
python/py_base_thread.c | 39 +++++++++++++++++++++++++++++++++++++
python/py_base_thread.h | 3 +++
python/py_module.c | 9 +++++++++
tests/python/metrics.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++++-
5 files changed, 106 insertions(+), 2 deletions(-)
diff --git a/include/distance.h b/include/distance.h
index 53be6d7..bb7140b 100644
--- a/include/distance.h
+++ b/include/distance.h
@@ -78,7 +78,10 @@ enum sr_distance_type
* Like the Levenshtein distance, but with transpositions. NOTE:
* The triangle inequality does not hold.
*/
- SR_DISTANCE_DAMERAU_LEVENSHTEIN
+ SR_DISTANCE_DAMERAU_LEVENSHTEIN,
+
+ /* Sentinel, keep it the last entry. */
+ SR_DISTANCE_NUM
};
float
diff --git a/python/py_base_thread.c b/python/py_base_thread.c
index 6536c52..8053878 100644
--- a/python/py_base_thread.c
+++ b/python/py_base_thread.c
@@ -23,14 +23,22 @@
#include "thread.h"
#include "frame.h"
+#include "distance.h"
#define thread_doc "satyr.BaseThread - base class for threads"
#define frames_doc "A list containing objects representing frames in a thread."
+#define distance_doc "Usage: thread.distance(other, dist_type=DISTANCE_LEVENSHTEIN)\n"\
+ "other: other thread\n"\
+ "dist_type (optional): one of DISTANCE_LEVENSHTEIN, DISTANCE_JARO_WINKLER,\n"\
+ " DISTANCE_JACCARD or DISTANCE_DAMERAU_LEVENSHTEIN\n"\
+ "Returns: positive float - distance between the two threads"
+
static PyMethodDef
thread_methods[] =
{
/* methods */
+ { "distance", (PyCFunction)sr_py_base_thread_distance, METH_VARARGS|METH_KEYWORDS, distance_doc },
{ NULL },
};
@@ -203,3 +211,34 @@ sr_py_base_thread_cmp(struct sr_py_base_thread *self, struct sr_py_base_thread *
return normalize_cmp(sr_thread_cmp(self->thread, other->thread));
}
+
+/* methods */
+PyObject *
+sr_py_base_thread_distance(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ PyObject *other;
+ int dist_type = SR_DISTANCE_LEVENSHTEIN;
+ static const char *kwlist[] = { "other", "dist_type", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|i", (char **)kwlist,
+ &sr_py_base_thread_type, &other, &dist_type))
+ return NULL;
+
+ struct sr_py_base_thread *t1 = (struct sr_py_base_thread *)self;
+ struct sr_py_base_thread *t2 = (struct sr_py_base_thread *)other;
+
+ if (self->ob_type != other->ob_type)
+ {
+ PyErr_SetString(PyExc_TypeError, "Both threads must have the same type");
+ return NULL;
+ }
+
+ if (dist_type < 0 || dist_type >= SR_DISTANCE_NUM)
+ {
+ PyErr_SetString(PyExc_ValueError, "Invalid distance type");
+ return NULL;
+ }
+
+ float dist = sr_distance(dist_type, t1->thread, t2->thread);
+ return PyFloat_FromDouble((double)dist);
+}
diff --git a/python/py_base_thread.h b/python/py_base_thread.h
index 69ae0de..663555c 100644
--- a/python/py_base_thread.h
+++ b/python/py_base_thread.h
@@ -49,6 +49,9 @@ PyObject *frames_to_python_list(struct sr_thread *thread, PyTypeObject *frame_ty
int sr_py_base_thread_cmp(struct sr_py_base_thread *self, struct sr_py_base_thread *other);
+/* methods */
+PyObject *sr_py_base_thread_distance(PyObject *self, PyObject *args, PyObject *kwds);
+
#ifdef __cplusplus
}
#endif
diff --git a/python/py_module.c b/python/py_module.c
index 32fd313..5be7da6 100644
--- a/python/py_module.c
+++ b/python/py_module.c
@@ -13,6 +13,7 @@
#include "py_java_stacktrace.h"
#include "py_metrics.h"
+#include "distance.h"
#include "gdb/sharedlib.h"
static PyMethodDef
@@ -130,6 +131,14 @@ init_satyr()
PyModule_AddObject(module, "Distances",
(PyObject *)&sr_py_distances_type);
+ PyModule_AddIntConstant(module, "DISTANCE_JARO_WINKLER",
+ SR_DISTANCE_JARO_WINKLER);
+ PyModule_AddIntConstant(module, "DISTANCE_JACCARD", SR_DISTANCE_JACCARD);
+ PyModule_AddIntConstant(module, "DISTANCE_LEVENSHTEIN",
+ SR_DISTANCE_LEVENSHTEIN);
+ PyModule_AddIntConstant(module, "DISTANCE_DAMERAU_LEVENSHTEIN",
+ SR_DISTANCE_DAMERAU_LEVENSHTEIN);
+
Py_INCREF(&sr_py_dendrogram_type);
PyModule_AddObject(module, "Dendrogram",
(PyObject *)&sr_py_dendrogram_type);
diff --git a/tests/python/metrics.py b/tests/python/metrics.py
index a97e5eb..703bbdc 100755
--- a/tests/python/metrics.py
+++ b/tests/python/metrics.py
@@ -1,8 +1,9 @@
#!/usr/bin/env python
-import os
import unittest
+from test_helpers import load_input_contents
+
try:
import _satyr as satyr
except ImportError:
@@ -43,5 +44,54 @@ class TestDistances(unittest.TestCase):
distances = satyr.Distances("levenshtein", [thread1, thread2], 2)
self.assertAlmostEqual(distances.get_distance(0, 1), 0.0)
+ def test_distance_gdb(self):
+ contents = load_input_contents('../gdb_stacktraces/rhbz-803600')
+ g = satyr.GdbStacktrace(contents)
+ thread1 = g.threads[0]
+ thread2 = g.threads[1]
+
+ # DISTANCE_LEVENSHTEIN is the default
+ self.assertAlmostEqual(thread1.distance(thread1), 0.0)
+ self.assertAlmostEqual(thread1.distance(thread2), 0.8827, places=3)
+ self.assertAlmostEqual(thread1.distance(thread2), thread2.distance(thread1))
+
+ # TODO are the same stacktraces supposed to compare at 0.98 ?
+ # I'll just leave the numbers here so we are assured they at least stay the same ...
+ self.assertAlmostEqual(
+ thread1.distance(thread1, dist_type=satyr.DISTANCE_JARO_WINKLER),
+ 0.98,
+ places=3)
+ self.assertAlmostEqual(
+ thread1.distance(thread2, dist_type=satyr.DISTANCE_JARO_WINKLER),
+ 0.3678,
+ places=3)
+ # WTF, not even symmetrical?
+ #self.assertAlmostEqual(
+ # thread1.distance(thread2, dist_type=satyr.DISTANCE_JARO_WINKLER),
+ # thread2.distance(thread1, dist_type=satyr.DISTANCE_JARO_WINKLER)
+ #)
+
+ self.assertAlmostEqual(
+ thread1.distance(thread1, dist_type=satyr.DISTANCE_JACCARD),
+ 0.0)
+ self.assertAlmostEqual(
+ thread1.distance(thread2, dist_type=satyr.DISTANCE_JACCARD),
+ 0.8904,
+ places=3)
+ self.assertAlmostEqual(
+ thread1.distance(thread2, dist_type=satyr.DISTANCE_JACCARD),
+ thread2.distance(thread1, dist_type=satyr.DISTANCE_JACCARD))
+
+ self.assertAlmostEqual(
+ thread1.distance(thread1, dist_type=satyr.DISTANCE_DAMERAU_LEVENSHTEIN),
+ 0.0)
+ self.assertAlmostEqual(
+ thread1.distance(thread2, dist_type=satyr.DISTANCE_DAMERAU_LEVENSHTEIN),
+ 0.8827,
+ places=3)
+ self.assertAlmostEqual(
+ thread1.distance(thread2, dist_type=satyr.DISTANCE_DAMERAU_LEVENSHTEIN),
+ thread2.distance(thread1, dist_type=satyr.DISTANCE_DAMERAU_LEVENSHTEIN))
+
if __name__ == '__main__':
unittest.main()
--
1.7.11.7
More information about the Crash-catcher
mailing list