object-oriented manner.
* Updated README.
* Updated setup script to include the high-level module, as well as
building the low-level extension.
rrdtool-py3k
============
-rrdtool bindings for Python 3
\ No newline at end of file
+Python 3 bindings for rrdtool with a native C extension and an object-oriented way to work with Round Robin Databases.
+
+The bindings are based on the code of the original Python 2 bindings for rrdtool by Hye-Shik Chang.
+
+Installation
+------------
+
+In order to build the native C extension (which is an required step), you'll need librrd and its headers installed. Having rrdtool installed should be enough on most distributions.
+
+**How to Install? **
+
+1. Download a copy of the repository.
+2. Run `python setup.py install` to build an install the native C extension as well as the RRD module.
+
+Usage
+-----
+
+You can either use the low-level `rrdtool` module (which offers almost the same functions as the old Python 2 bindings for rrdtool provided), or the `RRDtool` module, which represents a object-oriented interface to rrdtool.
+
+Unlike the Python 2 binding, this binding is able to create graphs entirely in-memory, which makes it ideal for generating a large amount of graphs without having high I/O. This feature is currently available on POSIX platforms only, because I wasn't able to find a portable way to redirect stdout to a memory allocated buffer (which is required for that). To use this feature, specify "-" as the output filename in graphs (low-level extension), or `None` in the high-level module.
+
+### Using the low-level `rrdtool` module
+
+```python
+import rrdtool
+
+# Create Round Robin Database
+rrdtool.create('test.rrd', '--start', 'now', '--step', '300', 'RRA:AVERAGE:0.5:1:1200', 'DS:temp:GAUGE:600:-273:5000')
+
+# Feed updates to the RRD
+rrdtool.update('test.rrd', 'N:32')
+```
+
+### Using the high-level `RRDtool` module
+
+```python
+import RRDtool
+
+# Create a Round Robin Database
+rrd = RRDtool.create('test.rrd', '--start', 'now', '--step', '300', 'RRA:AVERAGE:0.5:1:1200', 'DS:temp:GAUGE:600:-273:5000')
+
+# Update the RRD
+rrd.update([(None, 32)])
+
+# Create a graph from it
+rrd.graph('test.png', '--end', 'now', '--start', 'end-5minutes', '--width', '400', 'DEF:ds0a=test.rrd:temp:AVERAGE', 'LINE1:ds0a#0000FF:"temperature\l"')
+
+# Same, but keep data in memory.
+data = rrd.graph(None, '--end', 'now', '--start', 'end-5minutes', '--width', '400', 'DEF:ds0a=test.rrd:temp:AVERAGE', 'LINE1:ds0a#0000FF:"temperature\l"')
+
+# You can also use file-like objects
+from io import BytesIO
+rrd.graph(io, ...)
+```
+
+Author
+------
+
+Christian Jurk <commx@commx.ws>
+
+This binding was created because I am currently porting some existing Python 2 code to Python 3 and try to help the community by contributing a updated binding extension. Hope someone can benefit from it.
+
+If you encounter any bugs (which I expected at time of writing this), please submit them in the issue tracker here on the project page on Github. Thank you.
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+#
+# rrdtool-py3k, rrdtool bindings for Python 3.
+# Based on the rrdtool Python bindings for Python 2 from
+# Hye-Shik Chang <perky@fallin.lv>.
+#
+# Copyright 2012 Christian Jurk <commx@commx.ws>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+#
+#
+
+from datetime import datetime
+from io import BytesIO
+import os
+import rrdtool
+from time import mktime
+
+def create(filename, *args):
+ "Create a Round Robin Database and return a RRD object on success."
+ rrdtool.create(filename, *args)
+
+ if not os.access(filename, os.F_OK):
+ raise rrdtool.OperationalError('RRD file was not created')
+
+ return RRD(filename)
+
+class RRD:
+ """An object-based interface to the rrdtool module."""
+
+ def __init__(self, filename, check_type=True):
+ "Initialize the class instance with a filename."
+
+ if not os.access(filename, os.F_OK | os.R_OK):
+ raise rrdtool.OperationalError('RRD {!s} cannot be opened.' \
+ .format(filename))
+
+ # Use rrdinfo to test whether the file is a valid RRD file
+ if check_type is True:
+ rrdtool.info(filename)
+
+ self.readonly = not os.access(filename, os.W_OK)
+ self.filename = filename
+
+ def graph(self, output_file, *args):
+ "Create a graph based on one or more RRDs."
+ buffered = True
+ outfile = '-'
+
+ # write straigt into file using wrapper functions
+ if isinstance(output_file, str):
+ buffered = False
+ outfile = output_file
+
+ gdata = rrdtool.graph(outfile, *args)
+
+ if isinstance(gdata, tuple) and len(gdata) >= 4:
+ if output_file is None:
+ return gdata[3]
+ elif isinstance(output_file, BytesIO):
+ output_file.write(gdata[3])
+ return output_file
+
+ return None
+
+ def info(self):
+ return rrdtool.info(self.filename)
+
+ def update(self, values, *args):
+ vl = []
+
+ if self.readonly:
+ raise rrdtool.OperationalError('RRD file is read-only: {!s}' \
+ .format(self.filename))
+ elif not isinstance(values, (list, tuple)):
+ raise rrdtool.ProgrammingError('The values parameter must be a ' \
+ 'list or tuple')
+ else:
+ for row in values:
+ if isinstance(row, str):
+ vl.append(row)
+ elif isinstance(row, (list, tuple)):
+ if len(row) < 2:
+ raise rrdtool.ProgrammingError('Value {!r} has too ' \
+ 'few elements in sequence object'.format(row))
+ else:
+ ts = row[0]
+ if ts is None:
+ ts = 'N'
+ elif isinstance(ts, datetime):
+ ts = int(mktime(ts.timetuple()))
+ elif isinstance(ts, str):
+ ts = int(ts)
+ elif not isinstance(ts, int):
+ raise ValueError('Unsupported type')
+
+ v = '{}:{}'.format(ts, ':'.join([str(x) for x in row[1:]]))
+ vl.append(v)
+
+ arglist = tuple(vl + list(args))
+ return rrdtool.update(self.filename, *arglist)
+
+ def __repr__(self):
+ return '<RRD {!r}>'.format(self.filename)
static PyObject *
_rrdtool_util_info2dict(const rrd_info_t *data)
{
- PyObject *dict, *val = NULL;
+ PyObject *dict, *val;
dict = PyDict_New();
while (data) {
+ val = NULL;
+
switch (data->type) {
case RD_I_VAL:
if (isnan(data->value.u_val)) {
(char *)data->value.u_blo.ptr, data->value.u_blo.size);
break;
default:
- val = NULL;
break;
}
- if (val) {
+ if (val != NULL) {
PyDict_SetItemString(dict, data->key, val);
Py_DECREF(val);
}
return ret;
}
+static char _rrdtool_lib_version__doc__[] = "Get the version this binding "\
+ "was compiled against.";
+
+static PyObject *
+_rrdtool_lib_version(PyObject *self, PyObject *args)
+{
+ return PyUnicode_FromString(rrd_strversion());
+}
+
static PyMethodDef rrdtool_methods[] = {
{"create", (PyCFunction)_rrdtool_create,
METH_VARARGS, _rrdtool_create__doc__},
METH_VARARGS, _rrdtool_resize__doc__},
{"info", (PyCFunction)_rrdtool_info,
METH_VARARGS, _rrdtool_info__doc__},
+ {"lib_version", (PyCFunction)_rrdtool_lib_version,
+ METH_VARARGS, _rrdtool_lib_version__doc__},
{NULL, NULL, 0, NULL}
};
from distutils.core import setup, Extension
-from glob import glob
-sources = glob('*.c')
-module = Extension('rrdtool', sources=sources, libraries=['rrd'])
+def main():
+ module = Extension('rrdtool', sources=['rrdtool-py3k.c'],
+ libraries=['rrd'])
-setup(name='rrdtool',
- version='1.0',
- description='rrdtool bindings for Python 3',
- ext_modules=[module])
+ kwargs = dict(
+ name='python-rrdtool',
+ version='0.1.0',
+ description='rrdtool bindings for Python 3',
+ keywords=['rrdtool'],
+ author='Christian Jurk, Hye-Shik Chang',
+ author_email='commx@commx.ws',
+ ext_modules=[module],
+ py_modules=['RRDtool']
+ )
+ setup(**kwargs)
+
+if __name__ == '__main__':
+ main()