1. Ronald Oussoren
  2. asl

Commits

Ronald Oussoren  committed 3962824

Initial version for bindings to the ASL library on OSX.

This should work on OSX 10.4 or later, but I've only tested on
10.8 at the moment.

  • Participants
  • Branches default

Comments (0)

Files changed (22)

File .hgignore

View file
+doc/_build
+asl.egg-info
+build
+dist
+
+syntax: glob
+*.dSYM
+*.pyc
+*.pyo
+*.swp
+*.so

File MANIFEST.in

View file
+include README.txt
+include MANIFEST.in *.py
+graft doc
+graft doc/_static
+graft doc/_templates
+graft asl_tests
+global-exclude .DS_Store
+global-exclude *.pyc
+global-exclude *.so

File README.txt

View file
+Introduction
+============
+
+This package implements bindings to the ASL
+library on Mac OS X 10.4 or later. The ASL
+library is a low-level logging library, meant
+as a replacement for the syslog API.

File _asl.c

View file
+/*
+ * Bindings to the ASL library on Mac OS X
+ */
+#include "Python.h"
+#include <asl.h>
+
+typedef struct {
+    PyObject_HEAD
+
+    aslclient value;
+} ASLClientObject;
+
+typedef struct {
+    PyObject_HEAD
+
+    aslmsg value;
+    int owned;
+} ASLMessageObject;
+
+typedef struct {
+    PyObject_HEAD
+
+    aslresponse value;
+} ASLResponseObject;
+
+
+static PyObject* new_response(aslresponse value);
+static PyObject* new_message(aslmsg value, int owned);
+static PyObject* new_client(aslclient value);
+
+
+/* Response type */
+
+static void response_dealloc(PyObject* self);
+static PyObject* response_iter(PyObject* self);
+static PyObject* response_iternext(PyObject* self);
+
+static PyTypeObject ASLResponseType = {
+    PyVarObject_HEAD_INIT(&PyType_Type, 0)
+    .tp_name        = "asl.aslresponse",
+    .tp_basicsize   = sizeof(ASLResponseObject),
+    .tp_itemsize    = 0,
+    .tp_getattro    = PyObject_GenericGetAttr,
+    .tp_setattro    = PyObject_GenericSetAttr,
+    .tp_flags       = Py_TPFLAGS_DEFAULT,
+    .tp_iter        = response_iter,
+    .tp_iternext    = response_iternext,
+    .tp_dealloc     = response_dealloc,
+};
+
+static PyObject*
+new_response(aslresponse response)
+{
+    ASLResponseObject* result = PyObject_New(ASLResponseObject, &ASLResponseType);
+    if (result == NULL) {
+        aslresponse_free(response);
+        return NULL;
+    }
+
+    result->value = response;
+    return (PyObject*)result;
+}
+
+static void
+response_dealloc(PyObject* self)
+{
+    ASLResponseObject* r = (ASLResponseObject*)self;
+
+    aslresponse_free(r->value);
+    PyObject_DEL(self);
+}
+
+static PyObject*
+response_iter(PyObject* self)
+{
+    Py_INCREF(self);
+    return self;
+}
+
+static PyObject*
+response_iternext(PyObject* self)
+{
+    ASLResponseObject* r = (ASLResponseObject*)self;
+    aslmsg msg = aslresponse_next(r->value);
+
+    if (msg == NULL) {
+        /* End of iteration */
+        return NULL;
+    } else {
+        return new_message(msg, 0);
+    }
+}
+
+
+/* Message type */
+
+#define ASLMessage_Check(object) PyObject_TypeCheck((object), &ASLMessageType)
+#define ASLMessage_GET(object) (((ASLMessageObject*)(object))->value)
+
+static PyObject* message_new(PyTypeObject* cls, PyObject* args, PyObject* kwds);
+static void message_dealloc(PyObject* self);
+static PyObject* message_keys(PyObject* self);
+static PyObject* message_asdict(PyObject* self);
+static PyObject* message_set_query(PyObject* self, PyObject* args, PyObject* kwds);
+static PyObject* message_getitem(PyObject* self, PyObject* key);
+static int message_setitem(PyObject* self, PyObject* key, PyObject* value);
+
+
+static PyMethodDef message_methods[] = {
+	{
+		"keys",
+		(PyCFunction)message_keys,
+		METH_NOARGS,
+		"List of message attributes",
+	},
+	{
+		"asdict",
+		(PyCFunction)message_asdict,
+		METH_NOARGS,
+		"Dict with all attribute names and values",
+	},
+	{
+		"set_query",
+		(PyCFunction)message_set_query,
+		METH_VARARGS|METH_KEYWORDS,
+		"Set a query element",
+	},
+	{ 0, 0, 0, 0 } /* SENTINEL */
+};
+
+static PyMappingMethods message_mapping = {
+	/* No __len__ because it is expensive to calculate the number of attributes */
+	.mp_length = NULL,
+	.mp_subscript = message_getitem,
+	.mp_ass_subscript = message_setitem,
+};
+
+
+static PyTypeObject ASLMessageType = {
+    PyVarObject_HEAD_INIT(&PyType_Type, 0)
+    .tp_name        = "asl.aslmsg",
+    .tp_basicsize   = sizeof(ASLMessageObject),
+    .tp_itemsize    = 0,
+    .tp_getattro    = PyObject_GenericGetAttr,
+    .tp_setattro    = PyObject_GenericSetAttr,
+    .tp_flags       = Py_TPFLAGS_DEFAULT,
+    .tp_dealloc     = message_dealloc,
+    .tp_methods	    = message_methods,
+    .tp_new	    = message_new,
+    .tp_as_mapping  = &message_mapping,
+};
+
+static PyObject*
+new_message(aslmsg msg, int owned)
+{
+    ASLMessageObject* result = PyObject_New(ASLMessageObject, &ASLMessageType);
+    if (result == NULL) {
+        asl_free(msg);
+        return NULL;
+    }
+
+    result->value = msg;
+    result->owned = owned;
+    return (PyObject*)result;
+}
+
+static PyObject*
+message_new(PyTypeObject* cls, PyObject* args, PyObject* kwds)
+{
+static char* kw_args[] = { "type", NULL };
+	uint32_t type;
+	aslmsg msg;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "I", kw_args, (unsigned int*)&type)) {
+		return NULL;
+	}
+
+	if (type != ASL_TYPE_MSG && type != ASL_TYPE_QUERY) {
+		PyErr_SetString(PyExc_ValueError, "Invalid message type");
+		return NULL;
+	}
+
+	msg = asl_new(type);
+	if (msg == NULL) {
+		PyErr_SetFromErrno(PyExc_OSError);
+		return NULL;
+	}
+
+	return new_message(msg, 1);
+}
+
+
+
+static void
+message_dealloc(PyObject* self)
+{
+    ASLMessageObject* r = (ASLMessageObject*)self;
+
+    if (r->owned) {
+	    asl_free(r->value);
+    }
+    PyObject_DEL(self);
+}
+
+static PyObject*
+message_getitem(PyObject* self, PyObject* key)
+{
+	ASLMessageObject* r = (ASLMessageObject*)self;
+	PyObject* bytes = NULL;
+	const char* c_key;
+	const char* c_value;
+
+	if (PyUnicode_Check(key)) {
+		bytes = PyUnicode_AsUTF8String(key);
+		if (bytes == NULL) {
+			return NULL;
+		}
+
+		c_key = PyBytes_AsString(bytes);
+		if (c_key == NULL) {
+			Py_XDECREF(bytes);
+			return NULL;
+		}
+
+#if PY_MAJOR_VERSION == 2
+	} else if (PyString_Check(key)) {
+		c_key = PyString_AsString(key);
+#endif
+	} else {
+		PyErr_SetObject(PyExc_KeyError, key);
+		return NULL;
+	}
+
+	c_value = asl_get(r->value, c_key);
+	if (c_value == NULL) {
+		PyErr_SetString(PyExc_KeyError, c_key);
+		Py_XDECREF(bytes);
+		return NULL;
+	}
+
+	Py_XDECREF(bytes);
+	return Py_BuildValue("s", c_value);
+}
+
+static int
+message_setitem(PyObject* self, PyObject* key, PyObject* value)
+{
+	ASLMessageObject* r = (ASLMessageObject*)self;
+	PyObject* key_bytes = NULL;
+	PyObject* value_bytes = NULL;
+	const char* c_key;
+	const char* c_value;
+
+	if (PyUnicode_Check(key)) {
+		key_bytes = PyUnicode_AsUTF8String(key);
+		if (key_bytes == NULL) {
+			return -1;
+		}
+
+		c_key = PyBytes_AsString(key_bytes);
+		if (c_key == NULL) {
+			Py_XDECREF(key_bytes);
+			return -1;
+		}
+
+#if PY_MAJOR_VERSION == 2
+	} else if (PyString_Check(key)) {
+		c_key = PyString_AsString(key);
+#endif
+	} else {
+		PyErr_Format(PyExc_TypeError, "Expecting a string, got instance of '%s'", Py_TYPE(key)->tp_name);
+		return -1;
+	}
+
+	if (value == NULL) {
+		if (asl_unset(r->value, c_key) != 0) {
+			PyErr_SetFromErrno(PyExc_OSError);
+			Py_XDECREF(key_bytes);
+			return -1;
+		}
+		Py_XDECREF(key_bytes);
+		return 0;
+	}
+
+	if (PyUnicode_Check(value)) {
+		value_bytes = PyUnicode_AsUTF8String(value);
+		if (value_bytes == NULL) {
+			Py_XDECREF(key_bytes);
+			return -1;
+		}
+		c_value = PyBytes_AsString(value_bytes);;
+		if (c_value == NULL) {
+			Py_XDECREF(key_bytes);
+			Py_XDECREF(value_bytes);
+			return -1;
+		}
+
+#if PY_MAJOR_VERSION == 2
+	} else if (PyString_Check(value)) {
+		c_value = PyString_AsString(value);
+#endif
+	} else {
+		PyErr_Format(PyExc_TypeError, "Expecting a string, got instance of '%s'", Py_TYPE(value)->tp_name);
+		Py_XDECREF(key_bytes);
+		return -1;
+	}
+
+	if (asl_set(r->value, c_key, c_value) != 0) {
+		PyErr_SetFromErrno(PyExc_OSError);
+		Py_XDECREF(key_bytes);
+		Py_XDECREF(value_bytes);
+		return -1;
+	}
+	Py_XDECREF(key_bytes);
+	Py_XDECREF(value_bytes);
+	return 0;
+}
+
+
+static PyObject*
+message_keys(PyObject* self)
+{
+	ASLMessageObject* r = (ASLMessageObject*)self;
+	PyObject* result;
+	uint32_t n = 0;
+	const char* key;
+
+	result = PySet_New(NULL);
+	if (r == NULL) {
+		return NULL;
+	}
+
+	while (1) {
+		PyObject* o;
+
+		key = asl_key(r->value, n++);
+		if (key == NULL) {
+			break;
+		}
+
+#if PY_MAJOR_VERSION == 2
+		o = PyString_FromString(key);
+#else
+		o = PyUnicode_FromString(key);
+#endif
+		if (o == NULL) {
+			Py_DECREF(result);
+			return NULL;
+		}
+
+		if (PySet_Add(result, o) < 0) {
+			Py_DECREF(o);
+			return NULL;
+		}
+
+		Py_DECREF(o);
+	}
+	return result;
+}
+
+static PyObject*
+message_asdict(PyObject* self)
+{
+	ASLMessageObject* r = (ASLMessageObject*)self;
+	PyObject* result;
+	uint32_t n = 0;
+	const char* key;
+
+	result = PyDict_New();
+	if (r == NULL) {
+		return NULL;
+	}
+
+	while (1) {
+		PyObject* o;
+		const char* value;
+
+
+		key = asl_key(r->value, n++);
+		if (key == NULL) {
+			break;
+		}
+
+		value = asl_get(r->value, key);
+		if (value == NULL) {
+			/* Shouldn't happen */
+			PyErr_SetFromErrno(PyExc_OSError);
+			Py_DECREF(result);
+			return NULL;
+		}
+
+
+#if PY_MAJOR_VERSION == 2
+		o = PyString_FromString(value);
+#else
+		o = PyUnicode_FromString(value);
+#endif
+		if (o == NULL) {
+			Py_DECREF(result);
+			return NULL;
+		}
+
+		if (PyDict_SetItemString(result, key, o) < 0) {
+			Py_DECREF(o);
+			return NULL;
+		}
+
+		Py_DECREF(o);
+	}
+	return result;
+}
+
+static PyObject*
+message_set_query(PyObject* self, PyObject* args, PyObject* kwds)
+{
+	static char* kw_list[] = { "key", "value", "operation", NULL };
+	ASLMessageObject* r = (ASLMessageObject*)self;
+	const char* key;
+	const char* value;
+	uint32_t op;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "ssI", kw_list, &key, &value, &op)) {
+		return NULL;
+	}
+
+	if (asl_set_query(r->value, key, value, op) != 0) {
+		PyErr_SetFromErrno(PyExc_OSError);
+		return NULL;
+	}
+
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
+
+/* Client type */
+
+static PyObject* client_new(PyTypeObject* cls, PyObject* args, PyObject* kwds);
+static void client_dealloc(PyObject* self);
+static PyObject* client_add_log_file(PyObject* self, PyObject* args, PyObject* kwds);
+static PyObject* client_remove_log_file(PyObject* self, PyObject* args, PyObject* kwds);
+static PyObject* client_set_filter(PyObject* self, PyObject* args, PyObject* kwds);
+static PyObject* client_log(PyObject* self, PyObject* args, PyObject* kwds);
+static PyObject* client_send(PyObject* self, PyObject* args, PyObject* kwds);
+static PyObject* client_search(PyObject* self, PyObject* args, PyObject* kwds);
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8)
+static PyObject* client_log_descriptor(PyObject* self, PyObject* args, PyObject* kwds);
+#endif
+static PyObject* client_close(PyObject* self);
+static PyObject* client_enter(PyObject* self);
+static PyObject* client_exit(PyObject* self, PyObject* args);
+
+static PyMethodDef client_methods[] = {
+	{
+		"add_log_file",
+		(PyCFunction)client_add_log_file,
+		METH_VARARGS|METH_KEYWORDS,
+		NULL,
+	},
+	{
+		"remove_log_file",
+		(PyCFunction)client_remove_log_file,
+		METH_VARARGS|METH_KEYWORDS,
+		NULL,
+	},
+	{
+		"set_filter",
+		(PyCFunction)client_set_filter,
+		METH_VARARGS|METH_KEYWORDS,
+		NULL,
+	},
+	{
+		"log",
+		(PyCFunction)client_log,
+		METH_VARARGS|METH_KEYWORDS,
+		NULL,
+	},
+	{
+		"send",
+		(PyCFunction)client_send,
+		METH_VARARGS|METH_KEYWORDS,
+		NULL,
+	},
+	{
+		"search",
+		(PyCFunction)client_search,
+		METH_VARARGS|METH_KEYWORDS,
+		NULL,
+	},
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8)
+	{
+		"log_descriptor",
+		(PyCFunction)client_log_descriptor,
+		METH_VARARGS|METH_KEYWORDS,
+		NULL,
+	},
+#endif
+	{
+		"close",
+		(PyCFunction)client_close,
+		METH_NOARGS,
+		NULL,
+	},
+	{
+		"__enter__",
+		(PyCFunction)client_enter,
+		METH_NOARGS,
+		NULL,
+	},
+	{
+		"__exit__",
+		(PyCFunction)client_exit,
+		METH_VARARGS,
+		NULL,
+	},
+
+	{ 0, 0, 0, 0 } /* SENTINEL */
+};
+
+static PyTypeObject ASLClientType = {
+    PyVarObject_HEAD_INIT(&PyType_Type, 0)
+    .tp_name        = "asl.aslclient",
+    .tp_basicsize   = sizeof(ASLClientObject),
+    .tp_itemsize    = 0,
+    .tp_getattro    = PyObject_GenericGetAttr,
+    .tp_setattro    = PyObject_GenericSetAttr,
+    .tp_flags       = Py_TPFLAGS_DEFAULT,
+    .tp_dealloc     = client_dealloc,
+    .tp_methods	    = client_methods,
+    .tp_new	    = client_new,
+};
+
+static PyObject*
+new_client(aslclient cli)
+{
+    ASLClientObject* result = PyObject_New(ASLClientObject, &ASLClientType);
+    if (result == NULL) {
+        asl_close(cli);
+        return NULL;
+    }
+
+    result->value = cli;
+    return (PyObject*)result;
+}
+
+static PyObject*
+client_new(PyTypeObject* cls, PyObject* args, PyObject* kwds)
+{
+static char* kw_args[] = { "ident", "facility", "options", NULL };
+	const char* ident;
+	const char* facility;
+	uint32_t opts;
+	aslclient cli;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "zsI", kw_args, &ident, &facility, &opts)) {
+		return NULL;
+	}
+
+	cli = asl_open(ident, facility, opts);
+	if (cli == NULL) {
+		PyErr_SetFromErrno(PyExc_OSError);
+		return NULL;
+	}
+
+	return new_client(cli);
+}
+
+static void
+client_dealloc(PyObject* self)
+{
+    ASLClientObject* r = (ASLClientObject*)self;
+
+    if (r->value) {
+        asl_close(r->value);
+    }
+    PyObject_DEL(self);
+}
+
+static PyObject*
+client_add_log_file(PyObject* self, PyObject* args, PyObject* kwds)
+{
+	static char* kw_list[] = { "fd", NULL };
+    	ASLClientObject* r = (ASLClientObject*)self;
+	int fd;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kw_list, &fd)) {
+		return NULL;
+	}
+
+	if (r->value == NULL) {
+		PyErr_SetString(PyExc_ValueError, "Client is closed");
+		return NULL;
+	}
+
+	if (asl_add_log_file(r->value, fd) != 0) {
+		PyErr_SetFromErrno(PyExc_OSError);
+		return NULL;
+	}
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
+static PyObject*
+client_remove_log_file(PyObject* self, PyObject* args, PyObject* kwds)
+{
+	static char* kw_list[] = { "fd", NULL };
+    	ASLClientObject* r = (ASLClientObject*)self;
+	int fd;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kw_list, &fd)) {
+		return NULL;
+	}
+
+	if (r->value == NULL) {
+		PyErr_SetString(PyExc_ValueError, "Client is closed");
+		return NULL;
+	}
+
+	if (asl_remove_log_file(r->value, fd) != 0) {
+		PyErr_SetFromErrno(PyExc_OSError);
+		return NULL;
+	}
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
+static PyObject*
+client_set_filter(PyObject* self, PyObject* args, PyObject* kwds)
+{
+	static char* kw_list[] = { "filter", NULL };
+    	ASLClientObject* r = (ASLClientObject*)self;
+	int filter;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kw_list, &filter)) {
+		return NULL;
+	}
+
+	if (r->value == NULL) {
+		PyErr_SetString(PyExc_ValueError, "Client is closed");
+		return NULL;
+	}
+
+	filter = asl_set_filter(r->value, filter);
+	return Py_BuildValue("i", filter);
+}
+
+static PyObject*
+client_close(PyObject* self)
+{
+    	ASLClientObject* r = (ASLClientObject*)self;
+	if (r->value != NULL) {
+		asl_close(r->value);
+		r->value = NULL;
+	}
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
+static PyObject*
+client_enter(PyObject* self)
+{
+    	ASLClientObject* r = (ASLClientObject*)self;
+	if (r->value == NULL) {
+		PyErr_SetString(PyExc_ValueError, "Client is closed");
+		return NULL;
+	}
+	Py_INCREF(self);
+	return self;
+}
+
+static PyObject*
+client_exit(PyObject* self, PyObject* args)
+{
+    	ASLClientObject* r = (ASLClientObject*)self;
+	PyObject* exc_type;
+	PyObject* exc_val;
+	PyObject* exc_tb;
+
+	if (!PyArg_ParseTuple(args, "OOO", &exc_type, &exc_val, &exc_tb)) {
+		return NULL;
+	}
+
+	if (r->value != NULL) {
+		asl_close(r->value);
+		r->value = NULL;
+	}
+
+	Py_INCREF(Py_False);
+	return Py_False;
+}
+
+static PyObject*
+client_send(PyObject* self, PyObject* args, PyObject* kwds)
+{
+	static char* kw_list[] = { "msg", NULL };
+    	ASLClientObject* r = (ASLClientObject*)self;
+	PyObject* msg;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kw_list, &ASLMessageType, &msg)) {
+		return NULL;
+	}
+
+	if (r->value == NULL) {
+		PyErr_SetString(PyExc_ValueError, "Client is closed");
+		return NULL;
+	}
+
+	if (asl_send(r->value, ASLMessage_GET(msg)) != 0) {
+		PyErr_SetFromErrno(PyExc_OSError);
+		return NULL;
+	}
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
+static PyObject*
+client_search(PyObject* self, PyObject* args, PyObject* kwds)
+{
+	static char* kw_list[] = { "msg", NULL };
+    	ASLClientObject* r = (ASLClientObject*)self;
+	PyObject* msg;
+	aslresponse resp;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!", kw_list, &ASLMessageType, &msg)) {
+		return NULL;
+	}
+
+	if (r->value == NULL) {
+		PyErr_SetString(PyExc_ValueError, "Client is closed");
+		return NULL;
+	}
+
+	resp = asl_search(r->value, ASLMessage_GET(msg));
+	if (resp == NULL) {
+		/* It is not possible to detect the difference between 'no results'
+		 * and 'invalid query'.
+		 */
+		PyObject* res;
+		PyObject* tmp = PyTuple_New(0);
+		if (tmp == NULL) {
+			return NULL;
+		}
+		res = PyObject_GetIter(tmp);
+		Py_DECREF(tmp);
+		return res;
+	}
+
+	return new_response(resp);
+}
+
+static PyObject*
+client_log(PyObject* self, PyObject* args, PyObject* kwds)
+{
+	static char* kw_list[] = { "msg", "level", "text", NULL };
+    	ASLClientObject* r = (ASLClientObject*)self;
+	PyObject* msg;
+	int level;
+	const char* text;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "Ois", kw_list, &msg, &level, &text)) {
+		return NULL;
+	}
+
+	if (msg != Py_None && !ASLMessage_Check(msg)) {
+		PyErr_Format(PyExc_TypeError, "Expected aclmsg instance or None, got instance of '%s'",
+				Py_TYPE(msg)->tp_name);
+		return NULL;
+	}
+
+	if (r->value == NULL) {
+		PyErr_SetString(PyExc_ValueError, "Client is closed");
+		return NULL;
+	}
+
+	if (asl_log(r->value, msg == Py_None ? NULL : ASLMessage_GET(msg), level, "%s", text) != 0) {
+		PyErr_SetFromErrno(PyExc_OSError);
+		return NULL;
+	}
+
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8)
+static PyObject*
+client_log_descriptor(PyObject* self, PyObject* args, PyObject* kwds)
+{
+	static char* kw_list[] = { "msg", "level", "fd", "fd_type", NULL };
+    	ASLClientObject* r = (ASLClientObject*)self;
+	PyObject* msg;
+	int level;
+	int fd;
+	int fd_type;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "Oiii", kw_list, &msg, &level, &fd, &fd_type)) {
+		return NULL;
+	}
+
+	if (msg != Py_None && !ASLMessage_Check(msg)) {
+		PyErr_Format(PyExc_TypeError, "Expected aclmsg instance or None, got instance of '%s'",
+				Py_TYPE(msg)->tp_name);
+		return NULL;
+	}
+
+	if (r->value == NULL) {
+		PyErr_SetString(PyExc_ValueError, "Client is closed");
+		return NULL;
+	}
+
+	if (fd_type != ASL_LOG_DESCRIPTOR_WRITE && fd_type != ASL_LOG_DESCRIPTOR_READ) {
+		PyErr_SetString(PyExc_ValueError, "Invalid fd_type");
+		return NULL;
+	}
+
+	if (asl_log_descriptor(r->value, msg == Py_None ? NULL : ASLMessage_GET(msg), level, fd, fd_type) != 0) {
+		PyErr_SetFromErrno(PyExc_OSError);
+		return NULL;
+	}
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+#endif
+
+
+/* Global functions */
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7)
+static PyObject*
+log_auxiliary_location(PyObject* self __attribute__((__unused__)), PyObject* args, PyObject* kwds)
+{
+static char* kw_list[] = { "msg", "title", "uti", "url", NULL };
+
+	PyObject* msg;
+	char* title;
+	char* uti;
+	char* url;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!szs", kw_list, &ASLMessageType, &msg, &title, &uti, &url)) {
+		return NULL;
+	}
+
+	if (asl_log_auxiliary_location(ASLMessage_GET(msg), title, uti, url) != 0) {
+		return NULL;
+	}
+
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
+static PyObject*
+create_auxiliary_file(PyObject* self __attribute__((__unused__)), PyObject* args, PyObject* kwds)
+{
+static char* kw_list[] = { "msg", "title", "uti", NULL };
+
+	PyObject* msg;
+	char* title;
+	char* uti;
+	int fd;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!sz", kw_list, &ASLMessageType, &msg, &title, &uti)) {
+		return NULL;
+	}
+
+	if (asl_create_auxiliary_file(ASLMessage_GET(msg), title, uti, &fd) != 0) {
+		return NULL;
+	}
+
+	return Py_BuildValue("i", fd);
+}
+
+static PyObject*
+close_auxiliary_file(PyObject* self __attribute__((__unused__)), PyObject* args, PyObject* kwds)
+{
+static char* kw_list[] = { "fd", NULL };
+
+	int fd;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kw_list, &fd)) {
+		return NULL;
+	}
+
+	if (asl_close_auxiliary_file(fd)) {
+		PyErr_SetFromErrno(PyExc_OSError);
+		return NULL;
+	}
+
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
+static PyObject*
+open_from_file(PyObject* self __attribute__((__unused__)), PyObject* args, PyObject* kwds)
+{
+static char* kw_list[] = { "fd", "ident", "facility", NULL };
+
+	int fd;
+	const char* ident;
+	const char* facility;
+	aslclient cli;
+
+	if (!PyArg_ParseTupleAndKeywords(args, kwds, "izs", kw_list, &fd, &ident, &facility)) {
+		return NULL;
+	}
+
+	cli = asl_open_from_file(fd, ident, facility);
+	if (cli == NULL) {
+		PyErr_SetFromErrno(PyExc_OSError);
+		return NULL;
+	}
+
+	return new_client(cli);
+}
+#endif
+
+
+static PyMethodDef mod_methods[] = {
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7)
+	{
+		"log_auxiliary_location",
+		(PyCFunction)log_auxiliary_location,
+		METH_VARARGS|METH_KEYWORDS,
+		NULL
+	},
+	{
+		"create_auxiliary_file",
+		(PyCFunction)create_auxiliary_file,
+		METH_VARARGS|METH_KEYWORDS,
+		NULL
+	},
+	{
+		"close_auxiliary_file",
+		(PyCFunction)close_auxiliary_file,
+		METH_VARARGS|METH_KEYWORDS,
+		NULL
+	},
+	{
+		"open_from_file",
+		(PyCFunction)open_from_file,
+		METH_VARARGS|METH_KEYWORDS,
+		NULL
+	},
+#endif
+
+    { 0, 0, 0, 0 } /* SENTINEL */
+};
+
+
+/* Module init */
+
+static int
+setup_module(PyObject* m)
+{
+    if (PyType_Ready(&ASLResponseType) < 0) {
+        return -1;
+    }
+    if (PyType_Ready(&ASLMessageType) < 0) {
+        return -1;
+    }
+    if (PyType_Ready(&ASLClientType) < 0) {
+        return -1;
+    }
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8) && (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8)
+    if (asl_log_descriptor == NULL) {
+	    if (PyDict_DelItemString(ASLClientType.tp_dict, "log_descriptor") < 0) {
+		    return -1;
+	    }
+    }
+#endif
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7) && (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7)
+    if (asl_create_auxiliary_file == NULL) {
+	    if (PyDict_DelItemString(PyModule_GetDict(m), "create_auxiliary_file") < 0) {
+		    return -1;
+	    }
+	    if (PyDict_DelItemString(PyModule_GetDict(m), "close_auxiliary_file") < 0) {
+		    return -1;
+	    }
+    }
+    if (asl_log_auxiliary_location == NULL) {
+	    if (PyDict_DelItemString(PyModule_GetDict(m), "log_auxiliary_location") < 0) {
+		    return -1;
+	    }
+    }
+    if (asl_open_from_file == NULL) {
+	    if (PyDict_DelItemString(PyModule_GetDict(m), "open_from_file") < 0) {
+		    return -1;
+	    }
+    }
+
+#endif
+
+    if (PyModule_AddObject(m, "aslresponse", (PyObject*)&ASLResponseType) < 0) {
+        return -1;
+    }
+    if (PyModule_AddObject(m, "aslmsg", (PyObject*)&ASLMessageType) < 0) {
+        return -1;
+    }
+    if (PyModule_AddObject(m, "aslclient", (PyObject*)&ASLClientType) < 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+
+#if PY_MAJOR_VERSION == 2
+PyMODINIT_FUNC
+init_asl(void)
+{
+    PyObject* m;
+
+    m = Py_InitModule("asl._asl", mod_methods);
+    if (m == NULL) {
+        return;
+    }
+
+    (void)setup_module(m);
+}
+
+#else /* PY_MAJOR_VERSION == 3 */
+
+static PyModuleDef aslmodule = {
+    PyModuleDef_HEAD_INIT,
+    "asl._asl",
+    NULL,
+    -1,
+    mod_methods, NULL, NULL, NULL, NULL
+};
+
+PyMODINIT_FUNC
+PyInit__asl(void)
+{
+    PyObject* m;
+
+    m = PyModule_Create(&aslmodule);
+    if (m == NULL) {
+        return NULL;
+    }
+
+    if (setup_module(m) < 0) {
+        Py_DECREF(m);
+        return NULL;
+    }
+    return m;
+}
+#endif /* PY_MAJOR_VERSION == 3 */

File _scripts/extract_constants.py

View file
+#!/usr/bin/env python3
+"""
+Helper script for updating the asl._constants module
+
+Run this with new versions of the OSX SDK to update that
+file (and forget to check for new APIs as well)
+"""
+import os
+import re
+
+literals = []
+
+def is_int_literal(value):
+    try:
+        int(value, 0)
+        return True
+    except ValueError as msg:
+        return False
+
+def is_string_literal(value):
+    return value[0] == '"' and value[-1] == '"' and len(value) > 1 and '"' not in value[1:-1]
+
+COMMENT=re.compile('/\*(?:[^*]|(?:\*[^/]))*\*/')
+def strip_comment(line):
+    return ''.join(COMMENT.split(line))
+
+
+with open("/usr/include/asl.h", "r") as fp:
+    for line in fp:
+        if line.startswith("#define"):
+            line = strip_comment(line).strip()
+            try:
+                _, key, value = line.split(None, 2)
+            except ValueError:
+                continue
+
+            if is_int_literal(value):
+                literals.append((key, int(value, 0)))
+
+            elif is_string_literal(value):
+                literals.append((key, value[1:-1]))
+
+            else:
+                print("skip", repr(line), repr(key), repr(value))
+
+
+with open(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "asl", "_constants.py"), "w") as fp:
+    print("'''", file=fp)
+    print("ASL constant definitions", file=fp)
+    print("", file=fp)
+    print("THIS FILE IS GENERATED, DON'T UPDATE MANUALLY", file=fp)
+    print("'''", file=fp)
+    print("", file=fp)
+    for key, value in literals:
+        print("%s = %r"%(key, value), file=fp)

File asl/__init__.py

View file
+"""
+Bindings to the Apple System Log facility.
+"""
+from __future__ import absolute_import
+
+__version__ = "0.9"
+
+from ._asl import *
+from ._constants import *
+
+asl_open = aslclient
+asl_new = aslmsg
+
+def ASL_FILTER_MASK(level):
+    return 1 << level
+
+def ASL_FILTER_MASK_UPTO(level):
+    return (1 << (level+1)) - 1

File asl/_constants.py

View file
+'''
+ASL constant definitions
+
+THIS FILE IS GENERATED, DON'T UPDATE MANUALLY
+'''
+
+ASL_LEVEL_EMERG = 0
+ASL_LEVEL_ALERT = 1
+ASL_LEVEL_CRIT = 2
+ASL_LEVEL_ERR = 3
+ASL_LEVEL_WARNING = 4
+ASL_LEVEL_NOTICE = 5
+ASL_LEVEL_INFO = 6
+ASL_LEVEL_DEBUG = 7
+ASL_STRING_EMERG = 'Emergency'
+ASL_STRING_ALERT = 'Alert'
+ASL_STRING_CRIT = 'Critical'
+ASL_STRING_ERR = 'Error'
+ASL_STRING_WARNING = 'Warning'
+ASL_STRING_NOTICE = 'Notice'
+ASL_STRING_INFO = 'Info'
+ASL_STRING_DEBUG = 'Debug'
+ASL_QUERY_OP_CASEFOLD = 16
+ASL_QUERY_OP_PREFIX = 32
+ASL_QUERY_OP_SUFFIX = 64
+ASL_QUERY_OP_SUBSTRING = 96
+ASL_QUERY_OP_NUMERIC = 128
+ASL_QUERY_OP_REGEX = 256
+ASL_QUERY_OP_EQUAL = 1
+ASL_QUERY_OP_GREATER = 2
+ASL_QUERY_OP_GREATER_EQUAL = 3
+ASL_QUERY_OP_LESS = 4
+ASL_QUERY_OP_LESS_EQUAL = 5
+ASL_QUERY_OP_NOT_EQUAL = 6
+ASL_QUERY_OP_TRUE = 7
+ASL_KEY_TIME = 'Time'
+ASL_KEY_TIME_NSEC = 'TimeNanoSec'
+ASL_KEY_HOST = 'Host'
+ASL_KEY_SENDER = 'Sender'
+ASL_KEY_FACILITY = 'Facility'
+ASL_KEY_PID = 'PID'
+ASL_KEY_UID = 'UID'
+ASL_KEY_GID = 'GID'
+ASL_KEY_LEVEL = 'Level'
+ASL_KEY_MSG = 'Message'
+ASL_KEY_READ_UID = 'ReadUID'
+ASL_KEY_READ_GID = 'ReadGID'
+ASL_KEY_EXPIRE_TIME = 'ASLExpireTime'
+ASL_KEY_MSG_ID = 'ASLMessageID'
+ASL_KEY_SESSION = 'Session'
+ASL_KEY_REF_PID = 'RefPID'
+ASL_KEY_REF_PROC = 'RefProc'
+ASL_KEY_AUX_TITLE = 'ASLAuxTitle'
+ASL_KEY_AUX_UTI = 'ASLAuxUTI'
+ASL_KEY_AUX_URL = 'ASLAuxURL'
+ASL_KEY_AUX_DATA = 'ASLAuxData'
+ASL_KEY_OPTION = 'ASLOption'
+ASL_KEY_SENDER_INSTANCE = 'SenderInstance'
+ASL_TYPE_MSG = 0
+ASL_TYPE_QUERY = 1
+ASL_FILTER_MASK_EMERG = 1
+ASL_FILTER_MASK_ALERT = 2
+ASL_FILTER_MASK_CRIT = 4
+ASL_FILTER_MASK_ERR = 8
+ASL_FILTER_MASK_WARNING = 16
+ASL_FILTER_MASK_NOTICE = 32
+ASL_FILTER_MASK_INFO = 64
+ASL_FILTER_MASK_DEBUG = 128
+ASL_OPT_STDERR = 1
+ASL_OPT_NO_DELAY = 2
+ASL_OPT_NO_REMOTE = 4
+ASL_LOG_DESCRIPTOR_READ = 1
+ASL_LOG_DESCRIPTOR_WRITE = 2

File asl_tests/__init__.py

View file
+""" asl unit tests """

File asl_tests/test_client.py

View file
+import unittest
+import sys
+import os
+
+import asl
+
+try:
+    long
+except NameError:
+    long = int
+
+class TestASLClient (unittest.TestCase):
+    def test_basic_creation(self):
+        cli = asl.aslclient("ident", "facility", 0)
+        self.assertIsInstance(cli, asl.aslclient)
+
+        self.assertRaises(TypeError, asl.aslclient, "ident")
+        self.assertRaises(TypeError, asl.aslclient, "ident", "faclity", "option")
+        self.assertRaises(TypeError, asl.aslclient, "ident", "faclity", 0, "extra")
+        self.assertRaises(TypeError, asl.aslclient, "ident", 42, 0)
+        self.assertRaises(TypeError, asl.aslclient, 32, "facility", 42)
+
+    def test_filter(self):
+        cli = asl.aslclient("ident", "facility", 0)
+        self.assertIsInstance(cli, asl.aslclient)
+
+        f = cli.set_filter(250)
+        self.assertIsInstance(f, (int, long))
+
+        f = cli.set_filter(f)
+        self.assertEqual(f, 250)
+
+        self.assertRaises(TypeError, cli.set_filter, "debug")
+
+        cli.close()
+        self.assertRaises(ValueError, cli.set_filter, 0)
+
+    def test_context(self):
+
+        with asl.aslclient("ident", "facility", 0) as cli:
+            self.assertIsInstance(cli, asl.aslclient)
+
+        self.assertRaises(ValueError, cli.set_filter, 0)
+
+    def test_log(self):
+        cli = asl.aslclient("ident", "facility", 0)
+        self.assertIsInstance(cli, asl.aslclient)
+
+        # Can't easily verify...
+        cli.log(None, asl.ASL_LEVEL_NOTICE, "hello world")
+
+        self.assertRaises(TypeError, cli.log, "hello", asl.ASL_LEVEL_NOTICE, "hello world")
+
+        msg = asl.aslmsg(asl.ASL_TYPE_MSG)
+        cli.log(msg, asl.ASL_LEVEL_NOTICE, "hello world")
+
+        self.assertRaises(TypeError, cli.log, msg, asl.ASL_LEVEL_NOTICE, "hello world %s", "extra")
+        self.assertRaises(TypeError, cli.log, msg, asl.ASL_LEVEL_NOTICE)
+
+    def test_send(self):
+        cli = asl.aslclient("ident", "facility", 0)
+        self.assertIsInstance(cli, asl.aslclient)
+
+        msg = asl.aslmsg(asl.ASL_TYPE_MSG)
+        msg[asl.ASL_KEY_MSG] = "Breaking attempt!"
+
+        cli.send(msg)
+
+        self.assertRaises(TypeError, cli.send, None)
+        self.assertRaises(TypeError, cli.send, "hello")
+
+    @unittest.skipUnless(sys.version_info[0] == 3, "Python 3 tests")
+    def test_no_bytes(self):
+        self.assertRaises(TypeError, asl.aslclient, "ident", b"faclity", 0)
+        self.assertRaises(TypeError, asl.aslclient, b"ident", "faclity", 0)
+
+        cli = asl.aslclient("ident", "facility", 0)
+        self.assertIsInstance(cli, asl.aslclient)
+
+        self.assertRaises(TypeError, cli.log, None, asl.ASL_LEVEL_NOTICE, b"hello world")
+
+    @unittest.skipUnless(sys.version_info[0] == 2, "Python 2 tests")
+    def test_with_unicode(self):
+        cli = asl.aslclient(b"ident".decode('utf-8'), "facility", 0)
+        self.assertIsInstance(cli, asl.aslclient)
+
+        cli = asl.aslclient(b"ident", "facility".decode('utf-8'), 0)
+        self.assertIsInstance(cli, asl.aslclient)
+
+        cli.log(None, asl.ASL_LEVEL_NOTICE, b"hello world".decode('utf-8'))
+
+    def test_add_remove(self):
+        self.assertRaises(OSError, os.fstat, 99)
+
+        cli = asl.aslclient("ident", "facility", 0)
+        self.assertIsInstance(cli, asl.aslclient)
+
+        cli.add_log_file(99)
+        cli.remove_log_file(99)
+        #self.assertRaises(OSError, cli.add_log_file, 99)
+        #self.assertRaises(OSError, cli.remove_log_file, 99)
+
+        cli.add_log_file(2)
+        cli.remove_log_file(2)
+        cli.remove_log_file(2)
+        #self.assertRaises(OSError, cli.remove_log_file, 2)
+
+    def test_search(self):
+        cli = asl.aslclient("ident", "facility", 0)
+        self.assertIsInstance(cli, asl.aslclient)
+
+        msg = asl.aslmsg(asl.ASL_TYPE_QUERY)
+        msg.set_query(asl.ASL_KEY_FACILITY, "com.apple.console", asl.ASL_QUERY_OP_EQUAL)
+
+        info = None
+        for info in cli.search(msg):
+            self.assertIsInstance(info, asl.aslmsg)
+
+        self.assertIsNot(info, None)
+
+        msg = asl.aslmsg(asl.ASL_TYPE_QUERY)
+        msg.set_query(asl.ASL_KEY_FACILITY, "com.apple.nosuchapp", asl.ASL_QUERY_OP_EQUAL)
+        self.assertEqual(list(cli.search(msg)), [])
+
+        msg = asl.aslmsg(asl.ASL_TYPE_QUERY)
+        msg.set_query(asl.ASL_KEY_FACILITY, "com.apple.console", asl.ASL_QUERY_OP_EQUAL)
+        self.assertNotEqual(list(cli.search(msg)), [])
+
+    def test_redirection(self):
+        cli = asl.aslclient("ident", "facility", 0)
+        self.assertIsInstance(cli, asl.aslclient)
+
+        fd = os.open("/dev/null", os.O_WRONLY)
+
+        cli.log_descriptor(None, asl.ASL_LEVEL_NOTICE, fd, asl.ASL_LOG_DESCRIPTOR_WRITE)
+
+        self.assertRaises(ValueError, cli.log_descriptor, None, asl.ASL_LEVEL_NOTICE, fd, 44)
+        self.assertRaises(TypeError, cli.log_descriptor, "u", asl.ASL_LEVEL_NOTICE, fd, asl.ASL_LOG_DESCRIPTOR_WRITE)
+
+        #
+
+        cli = asl.aslclient("ident", "facility", 0)
+        self.assertIsInstance(cli, asl.aslclient)
+
+        fd = os.open("/dev/null", os.O_WRONLY)
+
+        msg = asl.aslmsg(asl.ASL_TYPE_MSG)
+        msg[asl.ASL_KEY_FACILITY] = "com.apple.console"
+        cli.log_descriptor(msg, asl.ASL_LEVEL_NOTICE, fd, asl.ASL_LOG_DESCRIPTOR_WRITE)
+
+    def test_open_from_file(self):
+        try:
+            fd = os.open("asl.log", os.O_RDWR|os.O_CREAT, 0o660)
+            cli = asl.open_from_file(fd, "ident", "facility")
+            self.assertIsInstance(cli, asl.aslclient)
+
+            #
+
+            fd = os.open("asl.log", os.O_RDONLY, 0o660)
+            self.assertRaises(OSError, asl.open_from_file, fd, "ident", "facility")
+
+        finally:
+            if os.path.exists("asl.log"):
+                os.unlink("asl.log")
+
+if __name__ == "__main__":
+    unittest.main()

File asl_tests/test_constants.py

View file
+import unittest
+
+import asl
+
+
+class TestConstant (unittest.TestCase):
+    def test_query_options(self):
+        self.assertEqual(asl.ASL_QUERY_OP_CASEFOLD, 0x0010)
+        self.assertEqual(asl.ASL_QUERY_OP_PREFIX, 0x0020)
+        self.assertEqual(asl.ASL_QUERY_OP_SUFFIX, 0x0040)
+        self.assertEqual(asl.ASL_QUERY_OP_SUBSTRING, 0x0060)
+        self.assertEqual(asl.ASL_QUERY_OP_NUMERIC, 0x0080)
+        self.assertEqual(asl.ASL_QUERY_OP_REGEX, 0x0100)
+        self.assertEqual(asl.ASL_QUERY_OP_EQUAL, 0x0001)
+        self.assertEqual(asl.ASL_QUERY_OP_GREATER, 0x0002)
+        self.assertEqual(asl.ASL_QUERY_OP_GREATER_EQUAL, 0x0003)
+        self.assertEqual(asl.ASL_QUERY_OP_LESS, 0x0004)
+        self.assertEqual(asl.ASL_QUERY_OP_LESS_EQUAL, 0x0005)
+        self.assertEqual(asl.ASL_QUERY_OP_NOT_EQUAL, 0x0006)
+        self.assertEqual(asl.ASL_QUERY_OP_TRUE, 0x0007)
+
+    def test_level_numbers(self):
+        self.assertEqual(asl.ASL_LEVEL_EMERG, 0)
+        self.assertEqual(asl.ASL_LEVEL_ALERT, 1)
+        self.assertEqual(asl.ASL_LEVEL_CRIT, 2)
+        self.assertEqual(asl.ASL_LEVEL_ERR, 3)
+        self.assertEqual(asl.ASL_LEVEL_WARNING, 4)
+        self.assertEqual(asl.ASL_LEVEL_NOTICE, 5)
+        self.assertEqual(asl.ASL_LEVEL_INFO, 6)
+        self.assertEqual(asl.ASL_LEVEL_DEBUG, 7)
+
+    def test_level_names(self):
+        self.assertEqual(asl.ASL_STRING_EMERG, "Emergency")
+        self.assertEqual(asl.ASL_STRING_ALERT, "Alert")
+        self.assertEqual(asl.ASL_STRING_CRIT, "Critical")
+        self.assertEqual(asl.ASL_STRING_ERR, "Error")
+        self.assertEqual(asl.ASL_STRING_WARNING, "Warning")
+        self.assertEqual(asl.ASL_STRING_NOTICE, "Notice")
+        self.assertEqual(asl.ASL_STRING_INFO, "Info")
+        self.assertEqual(asl.ASL_STRING_DEBUG, "Debug")
+
+    def test_standard_attributes(self):
+        self.assertEqual(asl.ASL_KEY_TIME, "Time")
+        self.assertEqual(asl.ASL_KEY_TIME_NSEC, "TimeNanoSec")
+        self.assertEqual(asl.ASL_KEY_HOST, "Host")
+        self.assertEqual(asl.ASL_KEY_SENDER, "Sender")
+        self.assertEqual(asl.ASL_KEY_FACILITY, "Facility")
+        self.assertEqual(asl.ASL_KEY_PID, "PID")
+        self.assertEqual(asl.ASL_KEY_UID, "UID")
+        self.assertEqual(asl.ASL_KEY_GID, "GID")
+        self.assertEqual(asl.ASL_KEY_LEVEL, "Level")
+        self.assertEqual(asl.ASL_KEY_MSG, "Message")
+        self.assertEqual(asl.ASL_KEY_READ_UID, "ReadUID")
+        self.assertEqual(asl.ASL_KEY_READ_GID, "ReadGID")
+        self.assertEqual(asl.ASL_KEY_EXPIRE_TIME, "ASLExpireTime")
+        self.assertEqual(asl.ASL_KEY_MSG_ID, "ASLMessageID")
+        self.assertEqual(asl.ASL_KEY_SESSION, "Session")
+        self.assertEqual(asl.ASL_KEY_REF_PID, "RefPID")
+        self.assertEqual(asl.ASL_KEY_REF_PROC, "RefProc")
+        self.assertEqual(asl.ASL_KEY_AUX_TITLE, "ASLAuxTitle")
+        self.assertEqual(asl.ASL_KEY_AUX_UTI, "ASLAuxUTI")
+        self.assertEqual(asl.ASL_KEY_AUX_URL, "ASLAuxURL")
+        self.assertEqual(asl.ASL_KEY_AUX_DATA, "ASLAuxData")
+        self.assertEqual(asl.ASL_KEY_OPTION, "ASLOption")
+        self.assertEqual(asl.ASL_KEY_SENDER_INSTANCE, "SenderInstance")
+
+    def test_message_types(self):
+        self.assertEqual(asl.ASL_TYPE_MSG, 0)
+        self.assertEqual(asl.ASL_TYPE_QUERY, 1)
+
+    def test_filter_masks(self):
+        self.assertEqual(asl.ASL_FILTER_MASK_EMERG, 0x01)
+        self.assertEqual(asl.ASL_FILTER_MASK_ALERT, 0x02)
+        self.assertEqual(asl.ASL_FILTER_MASK_CRIT, 0x04)
+        self.assertEqual(asl.ASL_FILTER_MASK_ERR, 0x08)
+        self.assertEqual(asl.ASL_FILTER_MASK_WARNING, 0x10)
+        self.assertEqual(asl.ASL_FILTER_MASK_NOTICE, 0x20)
+        self.assertEqual(asl.ASL_FILTER_MASK_INFO, 0x40)
+        self.assertEqual(asl.ASL_FILTER_MASK_DEBUG, 0x80)
+
+    def test_open_options(self):
+        self.assertEqual(asl.ASL_OPT_STDERR, 0x00000001)
+        self.assertEqual(asl.ASL_OPT_NO_DELAY, 0x00000002)
+        self.assertEqual(asl.ASL_OPT_NO_REMOTE, 0x00000004)
+
+    def test_descriptor_types(self):
+        self.assertEqual(asl.ASL_LOG_DESCRIPTOR_READ, 1)
+        self.assertEqual(asl.ASL_LOG_DESCRIPTOR_WRITE, 2)
+
+if __name__ == "__main__":
+    unittest.main()
+
+

File asl_tests/test_message.py

View file
+import unittest
+import operator
+import sys
+
+import asl
+
+class TestMessage (unittest.TestCase):
+    def test_creation(self):
+        m = asl.aslmsg(asl.ASL_TYPE_MSG)
+        self.assertIsInstance(m, asl.aslmsg)
+
+        m = asl.aslmsg(asl.ASL_TYPE_QUERY)
+        self.assertIsInstance(m, asl.aslmsg)
+
+        self.assertRaises(ValueError, asl.aslmsg, 42)
+        self.assertRaises(TypeError, asl.aslmsg)
+
+    def test_attributes(self):
+        m = asl.aslmsg(asl.ASL_TYPE_MSG)
+        self.assertIsInstance(m, asl.aslmsg)
+
+        self.assertEqual(m.keys(), set())
+        self.assertEqual(m.asdict(), {})
+
+        m['foo'] = 'bar'
+        m['baz'] = 'hello'
+
+        self.assertEqual(m.keys(), {'foo', 'baz'})
+        self.assertEqual(m.asdict(), {'foo': 'bar', 'baz': 'hello'})
+
+        self.assertEqual(m['foo'], 'bar')
+        self.assertEqual(m['baz'], 'hello')
+
+        self.assertRaises(TypeError, operator.setitem, m, 'aap', 42)
+        self.assertRaises(TypeError, operator.setitem, m, 42, 'aap')
+        self.assertRaises(KeyError, operator.getitem, m, 'nokey')
+
+        del m['baz']
+        self.assertEqual(m.keys(), {'foo'})
+        self.assertEqual(m.asdict(), {'foo': 'bar'})
+
+        del m['nokey']
+        self.assertEqual(m.keys(), {'foo'})
+        self.assertEqual(m.asdict(), {'foo': 'bar'})
+
+    @unittest.skipUnless(sys.version_info[0] == 3, "Python 3 tests")
+    def test_no_bytes_attributes(self):
+        m = asl.aslmsg(asl.ASL_TYPE_MSG)
+        self.assertIsInstance(m, asl.aslmsg)
+
+        self.assertRaises(TypeError, operator.setitem, m, b'aap', 'hello')
+        self.assertRaises(TypeError, operator.setitem, m, 'aap', b'hello')
+        self.assertRaises(TypeError, m.set_query, 'aap', b'hello', asl.ASL_QUERY_OP_EQUAL)
+        self.assertRaises(TypeError, m.set_query, b'aap', 'hello', asl.ASL_QUERY_OP_EQUAL)
+
+    @unittest.skipUnless(sys.version_info[0] == 2, "Python 2 tests")
+    def test_unicode_attributes(self):
+        m = asl.aslmsg(asl.ASL_TYPE_MSG)
+        self.assertIsInstance(m, asl.aslmsg)
+
+        m[b'aap'.decode('utf-8')] =  'hello'
+        m['noot'] =  b'world'.decode('utf-8')
+
+        self.assertEqual(m.asdict(), {'aap': 'hello', 'noot': 'world' })
+
+        m.set_query(b'key1'.decode('utf-8'), 'value1', asl.ASL_QUERY_OP_EQUAL)
+        m.set_query(b'key2'.decode('utf-8'), 'value2', asl.ASL_QUERY_OP_EQUAL)
+
+        self.assertEqual(m.asdict(), {'aap': 'hello', 'noot': 'world', 'key1': 'value1', 'key2': 'value2' })
+
+    def test_set_query(self):
+        m = asl.aslmsg(asl.ASL_TYPE_QUERY)
+        self.assertIsInstance(m, asl.aslmsg)
+
+        m.set_query("foo", "bar", asl.ASL_QUERY_OP_EQUAL)
+        self.assertEqual(m.keys(), {'foo'})
+
+        self.assertRaises(TypeError, m.set_query, 42, "foo", asl.ASL_QUERY_OP_EQUAL)
+        self.assertRaises(TypeError, m.set_query, "key", 42, asl.ASL_QUERY_OP_EQUAL)
+        self.assertRaises(TypeError, m.set_query, "key", "key", "hello")
+
+if __name__ == "__main__":
+    unittest.main()

File asl_tests/test_misc.py

View file
+import unittest
+
+import asl
+
+class TestMiscFunctions (unittest.TestCase):
+    def test_mask(self):
+        self.assertEqual(asl.ASL_FILTER_MASK(1), 1<<1)
+        self.assertEqual(asl.ASL_FILTER_MASK(2), 1<<2)
+        self.assertEqual(asl.ASL_FILTER_MASK(8), 1<<8)
+
+    def test_upto(self):
+        self.assertEqual(asl.ASL_FILTER_MASK_UPTO(0), 1)
+        self.assertEqual(asl.ASL_FILTER_MASK_UPTO(1), 3)
+        self.assertEqual(asl.ASL_FILTER_MASK_UPTO(8), 511)
+
+    def test_aliases(self):
+        self.assertIs(asl.asl_new, asl.aslmsg)
+        self.assertIs(asl.asl_open, asl.aslclient)
+
+if __name__ == "__main__":
+    unittest.main()

File demo.py

View file
+import logging
+import asl
+import sys
+import os
+
+_LOGGING2ASL = {
+    logging.DEBUG: asl.ASL_STRING_DEBUG,
+    logging.INFO: asl.ASL_STRING_INFO,
+    logging.WARNING: asl.ASL_STRING_WARNING,
+    logging.ERROR: asl.ASL_STRING_ERR,
+    logging.CRITICAL: asl.ASL_STRING_CRIT,
+    logging.FATAL: asl.ASL_STRING_ALERT,
+}
+def _logging2asl(lvl):
+    try:
+        return _LOGGING2ASL[lvl]
+    except KeyError:
+        r = asl.ASL_STRING_DEBUG
+        for k in sorted(_LOGGING2ASL):
+           if k < lvl:
+               r = _LOGGING2ASL[k]
+        return r
+
+class ASLConsoleHandler (logging.Handler):
+    def __init__(self, ident=None, level=asl.ASL_STRING_INFO):
+        logging.Handler.__init__(self)
+        self._asl = asl.aslclient(ident, level, 0)
+
+    def emit(self, record):
+        msg = asl.aslmsg(asl.ASL_TYPE_MSG)
+
+        # Add all attributes of the logging record
+        # to the ASL log message:
+        for k in dir(record):
+            if k in ('args', 'levelno', 'levelname', 'msecs', 'relativeCreated', 'asctime', 'created'):
+               continue
+            if k.startswith('_'):
+               continue
+
+            # What about exc_info?
+
+            msg["py." + k] = str(getattr(record, k))
+
+        # Then set up the default attributes:
+        msg[asl.ASL_KEY_FACILITY] = "com.apple.console"
+        msg[asl.ASL_KEY_LEVEL] = _logging2asl(record.levelno)
+        msg[asl.ASL_KEY_READ_UID] = str(os.getuid())
+        msg[asl.ASL_KEY_MSG] = self.format(record)
+
+        self._asl.send(msg)
+
+logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
+root = logging.getLogger()
+print root
+root.addHandler(ASLConsoleHandler())
+
+root.warning("test me")

File doc/Makefile

View file
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+BUILDDIR      = _build
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html       to make standalone HTML files"
+	@echo "  dirhtml    to make HTML files named index.html in directories"
+	@echo "  singlehtml to make a single large HTML file"
+	@echo "  pickle     to make pickle files"
+	@echo "  json       to make JSON files"
+	@echo "  htmlhelp   to make HTML files and a HTML help project"
+	@echo "  qthelp     to make HTML files and a qthelp project"
+	@echo "  devhelp    to make HTML files and a Devhelp project"
+	@echo "  epub       to make an epub"
+	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
+	@echo "  text       to make text files"
+	@echo "  man        to make manual pages"
+	@echo "  texinfo    to make Texinfo files"
+	@echo "  info       to make Texinfo files and run them through makeinfo"
+	@echo "  gettext    to make PO message catalogs"
+	@echo "  changes    to make an overview of all changed/added/deprecated items"
+	@echo "  linkcheck  to check all external links for integrity"
+	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+	-rm -rf $(BUILDDIR)/*
+
+html:
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+	@echo
+	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+json:
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+	@echo
+	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
+	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/ASL.qhcp"
+	@echo "To view the help file:"
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/ASL.qhc"
+
+devhelp:
+	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+	@echo
+	@echo "Build finished."
+	@echo "To view the help file:"
+	@echo "# mkdir -p $$HOME/.local/share/devhelp/ASL"
+	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/ASL"
+	@echo "# devhelp"
+
+epub:
+	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+	@echo
+	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+	@echo "Run \`make' in that directory to run these through (pdf)latex" \
+	      "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo "Running LaTeX files through pdflatex..."
+	$(MAKE) -C $(BUILDDIR)/latex all-pdf
+	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+	@echo
+	@echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+	@echo
+	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+	@echo
+	@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+	@echo "Run \`make' in that directory to run these through makeinfo" \
+	      "(use \`make info' here to do that automatically)."
+
+info:
+	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+	@echo "Running Texinfo files through makeinfo..."
+	make -C $(BUILDDIR)/texinfo info
+	@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+	$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+	@echo
+	@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+	@echo
+	@echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+	@echo "Testing of doctests in the sources finished, look at the " \
+	      "results in $(BUILDDIR)/doctest/output.txt."

File doc/api.rst

View file
+:mod:`asl` --- ASL library
+==========================
+
+.. module:: asl
+   :synopsis: ASL library bindings
+
+The Apple System Log facility is a logging API with a simular
+goal to the classic unix syslog API, but with a richer interface
+and with an API for querying the logging system.
+
+See Apple's manual page for the C API for more information on how
+to use this module.
+
+.. note::
+
+   All APIs accept unicode strings, and require them for Python 3.
+
+   The strings will be converted to the C strings expected by the
+   C API using the UTF-8 encoding.
+
+.. note::
+
+   Functions and methods raise :exc:`OSError` when the C API
+   fails.
+
+
+ASL connection
+--------------
+
+.. class:: aslclient(ident, facility, options)
+
+   :param ident:    Name of the sender
+   :param facility: A facility name
+   :param optons:   Option flags, see `Open options`_
+
+   Connection to the ASL backend. These objects implement
+   the context protocol and can be used with the "with"
+   statement.
+
+   .. method:: add_log_file(fd)
+
+      :param fd: A file descriptor
+
+      Log messages will be written to the file descriptor as well
+      as to the logging subsystem.
+
+   .. method:: remove_log_file(fd)
+
+      :param fd: A file descriptor
+
+      No longer write log messages to the file descriptor. The
+      file descriptor should be one that's added earlier with
+      :meth:`add_log_file`.
+
+   .. method:: set_filter(filter)
+
+      :param filter: an integer with the levels that should be sent to the server
+
+      *filter* is an bitwise or of values from `Message priority levels`_,
+      and only messages whose log level is set in *filter* will be
+      sent to the logging subsystem.
+
+      Returns the previous value of the filter.
+
+   .. method:: log(msg, level, text)
+
+      :param msg: an :class:`aslmsg` object or :data:`None`
+
+      :param level: a lot level from `Message priority levels`_
+
+      :param text: a message string for the log message
+
+      .. note::
+
+         The C API uses a printf-style format string instead of
+         a message. In Python you can use string formatting to
+         format the string.
+
+   .. method:: send(msg)
+
+      :param msg: an :class:`aslmsg` object
+
+      Send a log message to the logging subsystem.
+
+   .. method:: search(msg)
+
+      :param msg: an :class:`aslmsg` object
+      :returns: an iterator that yields the result messages.
+
+      Send a query message to the logging subsystem.
+
+   .. method:: log_descriptor(msg, level, fd, fd_type)
+
+      :param msg: an :class:`aslmsg` object or :data:`None`
+      :param level: a log level from `Message priority levels`_
+      :param fd: a file descriptor
+      :param fd_type: type of file descriptor, from `File descriptor types`_
+
+      If *fd_type* is :data:`ASL_LOG_DESCRIPTOR_READ` ASL will read lines
+      from the file descriptor and forward those lines to the logging
+      subsystem as log messages.
+
+      If *fd_type* is :data:`ASL_LOG_DESCRIPTOR_WRITE` the file descriptor
+      is closed and reopened as a pipe where the application can write
+      lines that will be converted to log messages.
+
+      The *msg* is a template for the log messages created by this API.
+
+      This method is available on OSX 10.8 or later.
+
+
+   .. method:: close()
+
+      Explicitly close the client.
+
+      .. note::
+
+         The connection will also de closed when the
+         object is garbage collected.
+
+
+.. function::  asl_open(ident, facility, options)
+
+   :param ident: A program identifier string, or :data:`None`.
+   :param facility: A facility name
+   :param options: Option flags, see `Open options`_
+
+   This is an alias for :class:`asclient`.
+
+.. function:: open_from_file(fd, ident, facility)
+
+   :param fd: A file descriptor, open for writing and reading
+   :param ident: A program identifier string, or :data:`None`.
+   :param facility: A facility name
+
+   Opens an ASL log file for writing using an existing
+   file descriptor, for example one returned by
+   :func:`create_auxiliary_file`. The file descriptor
+   must be open for reading and writing.
+
+   Avaliable on Mac OS X 10.7 or later.
+
+
+
+ASL messages
+------------
+
+.. class:: aslmsg(type)
+
+   .. method:: __getitem__(key)
+
+      :param key: An attribute name
+
+      Return the attribute value for *key*. The key is a unicode string.
+
+      See `Standard message attributes`_ for a list of standard attributes.
+
+
+   .. method:: __setitem__(key, value)
+
+      :param key: An attribute name
+      :param value: Value for the attribute, must be a string
+
+      Set the value for attribute *key* to *value*. Both arguments
+      are unicode strings.
+
+      See `Standard message attributes`_ for a list of standard attributes.
+
+   .. method:: __delitem__(key)
+
+      :param key: An attribute name
+
+      Remove an attribute from the message.
+
+   .. method:: set_query(key, value, operation)
+
+      :param key: An attribute name
+      :param value: Value to compare the attribute name with
+      :param operation: The comparison method
+
+      Add a query element to the message. The operation is ... .
+
+      A second call to :meth:`set_query` for the same *key* will
+      replace that query. Calls to :meth:`set_query` for different
+      values of *key* are combined into an AND query (that is, all
+      query elements must match).
+
+      .. note::
+
+         It is not possible to perform OR queries, to do those you'll
+         have to fetch and merge the various subsets yourself.
+
+      .. note::
+
+         For basic equality tests (:data:`ASL_QUERY_OP_EQUAL`) you can
+         also set the *key* and *value* using the mapping interface. That
+         is,
+
+         ::
+
+             m[key] = value
+
+         is equivalent to::
+
+             m.set_query(key, value, ASL_QUERY_OP_EQUAL)
+
+
+   .. method:: keys()
+
+      Returns the set of attribute names for this message.
+
+   .. method:: asdict()
+
+      Return a dict with all attributes of this message. Equivalent to::
+
+         { k: msg[k] for k in msg.keys() }
+
+      .. note::
+
+         It is not possible to retrieve the "operation" for query
+         messages, the C API doesn't provide this information.
+
+
+Utility functions
+-----------------
+
+.. function:: ASL_FILTER_MASK(level)
+
+   :param level: A message priority level
+
+   Converts one of the values from `Message priority levels` into
+   a bit mask that can be used with :meth:`aslclient.set_filter`.
+
+
+.. function:: ASL_FILTER_MASK_UPTO(level)
+
+   :param level: A message priority level
+
+   Returns a mask where all bits from :data:`ASL_LEVEL_DEBUG`
+   upto *level* are set.
+
+
+.. function:: create_auxiliary_file(msg, title, uti)
+
+   :param msg: An :class:`aslmsg` object
+   :param title: Title for the auxiliary file (for display in Console.app)
+   :param uti: UTI for the file format, or :data:`None`
+
+   Creates an auxiliary file that may be used to store arbitrary
+   data associated with the mssage. Returns a file descriptor
+   for the file. This file descriptor must be closed with
+   :func:`close_auxiliary_file`.
+
+   When *uti* is :data:`None` the system will use "public.data"
+   instead.
+
+   The Console.app application will show auxiliary file as an file
+   icon that can be opened.
+
+   This function is available on Mac OS X 10.7 or later.
+
+
+.. function:: log_auxiliary_location(msg, title, uti, url)
+
+   :param msg: An :class:`aslmsg` object
+   :param title: Title for the auxiliary file (for display in Console.app)
+   :param uti: UTI for the file format of the URL contents, or :data:`None`
+   :param url: String representation of an URL
+
+   Write a log message to the logging system with a URL in the message.
+
+   When *uti* is :data:`None` the system will use "public.data"
+   instead.
+
+   The Console.app application will show the URL as a clickable link.
+
+   This method is available on Mac OS X 10.7 or later.
+
+
+.. function:: close_auxiliary_file(fd)
+
+   :param fd: File descriptor returned by :func:`create_auxiliary_file`.
+
+   Close the file descriptor for an auxiliary file that was created
+   earlier with :meth:`aslmsg.create_auxiliary_file`. A side effect
+   of this is that the message is logged with the logging system.
+
+.. function:: asl_new(type)
+
+   This is an alias for :class:`aslmsg`
+
+
+
+Constants
+---------
+
+
+Message priority levels
+.......................
+
+.. data:: ASL_LEVEL_EMERG
+
+.. data::  ASL_LEVEL_ALERT
+
+.. data::  ASL_LEVEL_CRIT
+
+.. data::  ASL_LEVEL_ERR
+
+.. data::  ASL_LEVEL_WARNING
+
+.. data::  ASL_LEVEL_NOTICE
+
+.. data::  ASL_LEVEL_INFO
+
+.. data::  ASL_LEVEL_DEBUG
+
+
+Message priority level strings
+..............................
+
+These are the string representation of the constants in
+the `previous section <Message priority levels>`_, and are
+used as the value for the :data:`ASL_KEY_LEVEL` key in
+:class:`aslmsg` objects.
+
+.. data::  ASL_STRING_EMERG
+
+.. data::  ASL_STRING_ALERT
+
+.. data::  ASL_STRING_CRIT
+
+.. data::  ASL_STRING_ERR
+
+.. data::  ASL_STRING_WARNING
+
+.. data::  ASL_STRING_NOTICE
+
+.. data::  ASL_STRING_INFO
+
+.. data::  ASL_STRING_DEBUG
+
+
+Attribute matching operations
+.............................
+
+
+Modifiers
+~~~~~~~~~
+
+.. data::  ASL_QUERY_OP_CASEFOLD
+
+.. data::  ASL_QUERY_OP_PREFIX
+
+.. data::  ASL_QUERY_OP_SUFFIX
+
+.. data::  ASL_QUERY_OP_SUBSTRING
+
+.. data::  ASL_QUERY_OP_NUMERIC
+
+.. data::  ASL_QUERY_OP_REGEX
+
+
+Operators
+~~~~~~~~~
+
+.. data::  ASL_QUERY_OP_EQUAL
+
+.. data::  ASL_QUERY_OP_GREATER
+
+.. data::  ASL_QUERY_OP_GREATER_EQUAL
+
+.. data::  ASL_QUERY_OP_LESS
+
+.. data::  ASL_QUERY_OP_LESS_EQUAL
+
+.. data::  ASL_QUERY_OP_NOT_EQUAL
+
+.. data::  ASL_QUERY_OP_TRUE
+
+
+Standard message attributes
+...........................
+
+These are the names of well-known attributes of ASL messages,
+you can add other attributes as well but those won't be used
+by the ASL backend.
+
+.. data:: ASL_KEY_TIME
+
+   Timestamp.  Set automatically
+
+.. data:: ASL_KEY_TIME_NSEC
+
+   Nanosecond time.
+
+.. data:: ASL_KEY_HOST
+
+   Sender's address (set by the server).
+
+.. data:: ASL_KEY_SENDER
+
+   Sender's identification string.  Default is process name.
+
+.. data:: ASL_KEY_FACILITY
+
+   Sender's facility.  Default is "user".
+
+.. data:: ASL_KEY_PID
+
+   Sending process ID encoded as a string.  Set automatically.
+
+.. data:: ASL_KEY_UID
+
+   UID that sent the log message (set by the server).
+
+.. data:: ASL_KEY_GID
+
+   GID that sent the log message (set by the server).
+
+.. data:: ASL_KEY_LEVEL
+
+   Log level number encoded as a string.  See levels above.
+
+.. data:: ASL_KEY_MSG
+
+   Message text.
+
+.. data:: A