-rrdtool-py3k
-============
+python-rrdtool
+==============
-Python 3 bindings for rrdtool with a native C extension and an object-oriented way to work with Round Robin Databases.
+Python bindings for rrdtool with a native C extension and an object-oriented way to work with Round Robin Databases. As of version 0.1.1, Python 2 and 3 is supported.
The bindings are based on the code of the original Python 2 bindings for rrdtool by Hye-Shik Chang.
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? **
+**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.
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
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"')
+imgdata = 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, ...)
```
+Changes
+-------
+
+## 0.1.1
+
+*Released 2013-12-19*
+
+* Added support for Python 2.7 (other 2.x versions might also work, but its not tested)
+* Added dump command
+* Fixed some issues regarding generating graphs with `graphv` on Python 3.3
+
+*Please note:* The `graph` method in the object-oriented RRD class will now return a dict by default (as returned by graphv). Only if the `output_file` parameter is None, the actual graph image bytes are returned. Python 3.3 will return a bytes object whereas Python 2.x will return a str object.
+
+## 0.1.0
+
+*Released 2012-09-17*
+
+* Initial release.
+* Support for Python 3.x added
+* Updated documentation strings (`__doc__`) for each of the rrdtool functions
+
Author
------
-#!/usr/bin/env python2
+#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
-# rrdtool-py3k, rrdtool bindings for Python 3.
+# python-rrdtool, rrdtool bindings for Python.
# Based on the rrdtool Python bindings for Python 2 from
# Hye-Shik Chang <perky@fallin.lv>.
#
#
#
-from datetime import datetime
-from io import BytesIO
import os
import rrdtool
+
+from datetime import datetime
+from io import BytesIO
from time import mktime
def create(filename, *args):
self.filename = filename
def graph(self, output_file, *args):
- "Create a graph based on one or more RRDs."
- buffered = True
- outfile = '-'
+ """
+ Generate a graph based on the arguments passed to this function.
+
+ If output_file is None, "-" will be used as the output filename.
+ In that case, rrdtool returns the image bytes within its info dict.
+ """
+ outfile = '-' if output_file is None else output_file
- # write straigt into file using wrapper functions
- if isinstance(output_file, str):
- buffered = False
- outfile = output_file
+ # when writing to a file-like object, use output buffering
+ if isinstance(output_file, os.IOBase):
+ outfile = '-'
- gdata = rrdtool.graph(outfile, *args)
+ info = rrdtool.graphv(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
+ if isinstance(info, dict) and 'image' in info:
+ if isinstance(output_file, os.IOBase):
+ output_file.write(info['image'])
+ elif output_file is None:
+ return info['image']
- return None
+ return info
def info(self):
return rrdtool.info(self.filename)
/*
- * rrdtool-py3k, rrdtool bindings for Python 3.
+ * python-rrdtool, rrdtool bindings for Python.
* Based on the rrdtool Python bindings for Python 2 from
* Hye-Shik Chang <perky@fallin.lv>.
*
#include <Python.h>
#include <rrd.h>
-static const char *_version = "0.1.0";
+static const char *_version = "0.1.1";
/* Exception types */
static PyObject *rrdtool_OperationalError;
break;
case RD_I_BLO:
- val = PyUnicode_FromStringAndSize(
+#if PY_MAJOR_VERSION >= 3
+ val = PyBytes_FromStringAndSize(
+ (char *)data->value.u_blo.ptr, data->value.u_blo.size);
+#else
+ val = PyString_FromStringAndSize(
(char *)data->value.u_blo.ptr, data->value.u_blo.size);
+#endif
break;
default:
break;
[--step|-s step]\n\
[DS:ds-name:DST:heartbeat:min:max]\n\
[RRA:CF:xff:steps:rows]\n\n\
- Full documentation can be found at:\n\
- http://oss.oetiker.ch/rrdtool/doc/rrdcreate.en.html";
+ Full documentation can be found at:\n\
+ http://oss.oetiker.ch/rrdtool/doc/rrdcreate.en.html";
static PyObject *
_rrdtool_create(PyObject *self, PyObject *args)
return ret;
}
+static char _rrdtool_dump__doc__[] = "Dump an RRD to XML.\n\n\
+ Usage: dump(args..)\n\
+ Arguments:\n\n\
+ [-h|--header {none,xsd,dtd}\n\
+ [--no-header]\n\
+ file.rrd\n\
+ [file.xml]\n\n\
+ Full documentation can be found at:\n\
+ http://oss.oetiker.ch/rrdtool/doc/rrddump.en.html";
+
+static PyObject *
+_rrdtool_dump(PyObject *self, PyObject *args)
+{
+ PyObject *ret;
+
+ if (convert_args("dump", args) == -1)
+ return NULL;
+
+ if (rrd_dump(rrdtool_argc, rrdtool_argv) != 0) {
+ PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
+ rrd_clear_error();
+ ret = NULL;
+ } else {
+ Py_INCREF(Py_None);
+ ret = Py_None;
+ }
+
+ destroy_args();
+ return ret;
+}
+
static char _rrdtool_update__doc__[] = "Store a new set of values into\
the RRD.\n\n\
Usage: update(args..)\n\
[--template|-t ds-name[:ds-name]...]\n\
N|timestamp:value[:value...]\n\
[timestamp:value[:value...] ...]\n\n\
- Full documentation can be found at:\n\
- http://oss.oetiker.ch/rrdtool/doc/rrdupdate.en.html";
+ Full documentation can be found at:\n\
+ http://oss.oetiker.ch/rrdtool/doc/rrdupdate.en.html";
static PyObject *
_rrdtool_update(PyObject *self, PyObject *args)
[--resolution|-r resolution]\n\
[--start|-s start]\n\
[--end|-e end]\n\n\
- Full documentation can be found at:\n\
- http://oss.oetiker.ch/rrdtool/doc/rrdfetch.en.html";
+ Full documentation can be found at:\n\
+ http://oss.oetiker.ch/rrdtool/doc/rrdfetch.en.html";
static PyObject *
_rrdtool_fetch(PyObject *self, PyObject *args)
[--daemon address]\n\
filename\n\
[filename ...]\n\n\
- Full documentation can be found at:\n\
- http://oss.oetiker.ch/rrdtool/doc/rrdflushcached.en.html";
+ Full documentation can be found at:\n\
+ http://oss.oetiker.ch/rrdtool/doc/rrdflushcached.en.html";
static PyObject *
_rrdtool_flushcached(PyObject *self, PyObject *args)
DEF:vname=rrdfile:ds-name:CF[:step=step][:start=time][:end=time]\n\
CDEF:vname=RPN expression\n\
VDEF=vname:RPN expression\n\n\
- Full documentation can be found at:\n\
- http://oss.oetiker.ch/rrdtool/doc/rrdgraph.en.html";
+ Full documentation can be found at:\n\
+ http://oss.oetiker.ch/rrdtool/doc/rrdgraph.en.html";
static PyObject *
_rrdtool_graph(PyObject *self, PyObject *args, PyObject *kwargs)
{
PyObject *ret;
int xsize, ysize, i;
- int keep_in_mem = 0;
double ymin, ymax;
- char **calcpr, *bp;
-#ifdef _POSIX_C_SOURCE
- FILE *orig_stdout = stdout;
- size_t bsize;
-#endif
+ char **calcpr;
if (convert_args("graph", args) == -1)
return NULL;
- if (rrdtool_argc >= 2 && strcmp(rrdtool_argv[1], "-") == 0) {
-#ifdef _POSIX_C_SOURCE
- keep_in_mem = 1;
-#else
- PyErr_SetString(rrdtool_ProgrammingError,
- "Output filename cannot be '-', because this platform does not "\
- "support output buffering");
- destroy_args();
- return NULL;
-#endif
- }
-
-#ifdef _POSIX_C_SOURCE
- if (keep_in_mem)
- stdout = open_memstream(&bp, &bsize);
-#endif
-
if (rrd_graph(rrdtool_argc, rrdtool_argv, &calcpr, &xsize, &ysize, NULL,
&ymin, &ymax) == -1) {
PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
rrd_clear_error();
ret = NULL;
} else {
- ret = PyTuple_New(keep_in_mem ? 4 : 3);
+ ret = PyTuple_New(3);
PyTuple_SET_ITEM(ret, 0, PyLong_FromLong((long)xsize));
PyTuple_SET_ITEM(ret, 1, PyLong_FromLong((long)ysize));
Py_INCREF(Py_None);
PyTuple_SET_ITEM(ret, 2, Py_None);
}
-
- /* feed buffered contents into a PyBytes object */
- if (keep_in_mem) {
- PyObject *pb;
-
- fflush(stdout);
- pb = PyBytes_FromStringAndSize(bp, bsize);
-
- PyTuple_SET_ITEM(ret, 3, pb);
- }
- }
-
- if (keep_in_mem) {
- fclose(stdout);
- stdout = orig_stdout;
}
destroy_args();
[-a|--maximum ds-name:max]\n\
[-d|--data-source-type ds-name:DST]\n\
[-r|--data-source-rename old-name:new-name]\n\n\
- Full documentation can be found at:\n\
- http://oss.oetiker.ch/rrdtool/doc/rrdtune.en.html";
+ Full documentation can be found at:\n\
+ http://oss.oetiker.ch/rrdtool/doc/rrdtune.en.html";
static PyObject *
_rrdtool_tune(PyObject *self, PyObject *args)
Arguments:\n\n\
filename\n\
[--rraindex number]\n\n\
- Full documentation can be found at:\n\
- http://oss.oetiker.ch/rrdtool/doc/rrdfirst.en.html";
+ Full documentation can be found at:\n\
+ http://oss.oetiker.ch/rrdtool/doc/rrdfirst.en.html";
static PyObject *
_rrdtool_first(PyObject *self, PyObject *args)
Arguments:\n\n\
filename\n\
[--daemon address]\n\n\
- Full documentation can be found at:\n\
- http://oss.oetiker.ch/rrdtool/doc/rrdlast.en.html";
+ Full documentation can be found at:\n\
+ http://oss.oetiker.ch/rrdtool/doc/rrdlast.en.html";
static PyObject *
_rrdtool_last(PyObject *self, PyObject *args)
rra-num\n\
GROW|SHRINK\n\
rows\n\n\
- Full documentation can be found at:\n\
- http://oss.oetiker.ch/rrdtool/doc/rrdlast.en.html";
+ Full documentation can be found at:\n\
+ http://oss.oetiker.ch/rrdtool/doc/rrdlast.en.html";
static PyObject *
_rrdtool_resize(PyObject *self, PyObject *args)
Usage: info(filename)\n\
Arguments:\n\n\
filename\n\n\
- Full documentation can be found at:\n\
- http://oss.oetiker.ch/rrdtool/doc/rrdinfo.en.html";
+ Full documentation can be found at:\n\
+ http://oss.oetiker.ch/rrdtool/doc/rrdinfo.en.html";
static PyObject *
_rrdtool_info(PyObject *self, PyObject *args)
static PyMethodDef rrdtool_methods[] = {
{"create", (PyCFunction)_rrdtool_create,
METH_VARARGS, _rrdtool_create__doc__},
+ {"dump", (PyCFunction)_rrdtool_dump,
+ METH_VARARGS, _rrdtool_dump__doc__},
{"update", (PyCFunction)_rrdtool_update,
METH_VARARGS, _rrdtool_update__doc__},
{"updatev", (PyCFunction)_rrdtool_updatev,
{NULL, NULL, 0, NULL}
};
+#if PY_MAJOR_VERSION >= 3
+
static struct PyModuleDef rrdtoolmodule = {
PyModuleDef_HEAD_INIT,
"rrdtool",
rrdtool_methods
};
+#endif
+
+#if PY_MAJOR_VERSION >= 3
PyMODINIT_FUNC
PyInit_rrdtool(void)
+#else
+void
+initrrdtool(void)
+#endif
{
PyObject *m;
+#if PY_MAJOR_VERSION >= 3
m = PyModule_Create(&rrdtoolmodule);
+#else
+ m = Py_InitModule3("rrdtool",
+ rrdtool_methods,
+ "rrdtool bindings for Python");
+#endif
+
if (m == NULL)
- return NULL;
+#if PY_MAJOR_VERSION >= 3
+ return NULL;
+#else
+ return;
+#endif
rrdtool_ProgrammingError = PyErr_NewException("rrdtool.ProgrammingError",
NULL, NULL);
PyModule_AddObject(m, "OperationalError", rrdtool_OperationalError);
PyModule_AddObject(m, "__version__", PyUnicode_FromString(_version));
+#if PY_MAJOR_VERSION >= 3
return m;
+#endif
}