]> git.ipfire.org Git - people/ms/python-rrdtool.git/blame - rrdtool-py3k.c
Changed License to LGPLv3.
[people/ms/python-rrdtool.git] / rrdtool-py3k.c
CommitLineData
ce6564c2 1/*
618986c4
CJ
2 * rrdtool-py3k, rrdtool bindings for Python 3.
3 * Based on the rrdtool Python bindings for Python 2 from
4 * Hye-Shik Chang <perky@fallin.lv>.
ce6564c2
CJ
5 *
6 * Copyright 2012 Christian Jurk <commx@commx.ws>
7 *
8 * This program is free software; you can redistribute it and/or modify
275aa95d
CJ
9 * it under the terms of the GNU Lesser General Public License as
10 * published by the Free Software Foundation; either version 3 of the
11 * License, or (at your option) any later version.
ce6564c2
CJ
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
275aa95d 18 * You should have received a copy of the GNU Lesser General Public License
ce6564c2
CJ
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21 * MA 02110-1301, USA.
22 *
ce6564c2
CJ
23 */
24
25#include <Python.h>
26#include <rrd.h>
27
06fe4e5b
CJ
28static const char *_version = "0.1.0";
29
ce6564c2
CJ
30/* Exception types */
31static PyObject *rrdtool_OperationalError;
32static PyObject *rrdtool_ProgrammingError;
33
618986c4
CJ
34static char **rrdtool_argv = NULL;
35static int rrdtool_argc = 0;
36
37static void
38destroy_args(void)
39{
40 PyMem_Del(rrdtool_argv);
41 rrdtool_argv = NULL;
42}
43
44/* Helper function to convert Python objects into a representation that the
45 * rrdtool functions can work with.
46 */
47static int
48convert_args(char *command, PyObject *args)
49{
50 PyObject *o, *lo;
51 int i, j, args_count, argv_count, element_count;
52
53 args_count = PyTuple_Size(args);
54 element_count = 0;
55 for (i = 0; i < args_count; i++)
56 {
57 o = PyTuple_GET_ITEM(args, i);
58 if (PyUnicode_Check(o) || PyBytes_Check(o))
59 element_count++;
60 else if (PyList_CheckExact(o))
61 element_count += PyList_Size(o);
62 else {
63 PyErr_Format(rrdtool_ProgrammingError,
64 "Argument %d must be string, bytes or list of " \
65 "string/bytes", i);
66 return -1;
67 }
68 }
69
70 rrdtool_argv = PyMem_New(char *, element_count + 1);
71
72 if (rrdtool_argv == NULL)
73 return -1;
74
75 argv_count = 0;
76 for (i = 0; i < args_count; i++) {
77 o = PyTuple_GET_ITEM(args, i);
78
79 if (PyUnicode_Check(o))
80 rrdtool_argv[++argv_count] = PyBytes_AsString(
81 PyUnicode_AsUTF8String(o));
82 else if (PyBytes_Check(o))
83 rrdtool_argv[++argv_count] = PyBytes_AS_STRING(o);
84 else if (PyList_CheckExact(o)) {
85 for (j = 0; j < PyList_Size(o); j++) {
86 lo = PyList_GetItem(o, j);
87 if (PyUnicode_Check(lo))
88 rrdtool_argv[++argv_count] = PyBytes_AS_STRING(
89 PyUnicode_AsUTF8String(lo));
90 else if (PyBytes_Check(lo))
91 rrdtool_argv[++argv_count] = PyBytes_AS_STRING(lo);
92 else {
93 PyMem_Del(rrdtool_argv);
94 PyErr_Format(rrdtool_ProgrammingError,
95 "Element %d in argument %d must be string", j, i);
96 return -1;
97 }
98 }
99 } else {
100 PyMem_Del(rrdtool_argv);
101 PyErr_Format(rrdtool_ProgrammingError,
102 "Argument %d must be string or list of strings", i);
103 return -1;
104 }
105 }
106
107 rrdtool_argv[0] = command;
108 rrdtool_argc = element_count + 1;
109
110 return 0;
111}
112
06fe4e5b
CJ
113static PyObject *
114_rrdtool_util_info2dict(const rrd_info_t *data)
115{
b4570f2a 116 PyObject *dict, *val;
06fe4e5b
CJ
117
118 dict = PyDict_New();
119
120 while (data) {
b4570f2a
CJ
121 val = NULL;
122
06fe4e5b
CJ
123 switch (data->type) {
124 case RD_I_VAL:
125 if (isnan(data->value.u_val)) {
126 Py_INCREF(Py_None);
127 val = Py_None;
128 } else
129 PyFloat_FromDouble(data->value.u_val);
130 break;
131
132 case RD_I_CNT:
133 val = PyLong_FromUnsignedLong(data->value.u_cnt);
134 break;
135
136 case RD_I_INT:
137 val = PyLong_FromLong(data->value.u_int);
138 break;
139
140 case RD_I_STR:
141 val = PyUnicode_FromString(data->value.u_str);
142 break;
143
144 case RD_I_BLO:
145 val = PyUnicode_FromStringAndSize(
146 (char *)data->value.u_blo.ptr, data->value.u_blo.size);
147 break;
148 default:
06fe4e5b
CJ
149 break;
150 }
151
b4570f2a 152 if (val != NULL) {
06fe4e5b
CJ
153 PyDict_SetItemString(dict, data->key, val);
154 Py_DECREF(val);
155 }
156
157 data = data->next;
158 }
159
160 return dict;
161}
162
618986c4
CJ
163static char _rrdtool_create__doc__[] = "Create a new Round Robin Database.\n\n\
164 Usage: create(args...)\n\
165 Arguments:\n\n\
166 filename\n\
167 [--start|-b start time]\n\
168 [--step|-s step]\n\
169 [DS:ds-name:DST:heartbeat:min:max]\n\
170 [RRA:CF:xff:steps:rows]\n\n\
171 Full documentation can be found at:\n\
172 http://oss.oetiker.ch/rrdtool/doc/rrdcreate.en.html";
173
ce6564c2
CJ
174static PyObject *
175_rrdtool_create(PyObject *self, PyObject *args)
176{
618986c4 177 PyObject *ret;
ce6564c2 178
618986c4
CJ
179 if (convert_args("create", args) == -1)
180 return NULL;
ce6564c2 181
618986c4 182 if (rrd_create(rrdtool_argc, rrdtool_argv) == -1) {
ce6564c2
CJ
183 PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
184 rrd_clear_error();
618986c4
CJ
185 ret = NULL;
186 } else {
187 Py_INCREF(Py_None);
188 ret = Py_None;
189 }
190
191 destroy_args();
192 return ret;
193}
194
195static char _rrdtool_update__doc__[] = "Store a new set of values into\
196 the RRD.\n\n\
197 Usage: update(args..)\n\
198 Arguments:\n\n\
199 filename\n\
200 [--template|-t ds-name[:ds-name]...]\n\
201 N|timestamp:value[:value...]\n\
202 [timestamp:value[:value...] ...]\n\n\
203 Full documentation can be found at:\n\
204 http://oss.oetiker.ch/rrdtool/doc/rrdupdate.en.html";
205
206static PyObject *
207_rrdtool_update(PyObject *self, PyObject *args)
208{
209 PyObject *ret;
210
211 if (convert_args("update", args) == -1)
212 return NULL;
213
214 if (rrd_update(rrdtool_argc, rrdtool_argv) == -1) {
215 PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
216 rrd_clear_error();
217 ret = NULL;
218 } else {
219 Py_INCREF(Py_None);
220 ret = Py_None;
221 }
222
223 destroy_args();
224 return ret;
225}
226
06fe4e5b
CJ
227static char _rrdtool_updatev__doc__[] = "Store a new set of values into "\
228 "the Round Robin Database and return an info dictionary.\n\n\
229 This function works in the same manner as 'update', but will return an\n\
230 info dictionary instead of None.";
231
232static PyObject *
233_rrdtool_updatev(PyObject *self, PyObject *args)
234{
235 PyObject *ret;
236 rrd_info_t *data;
237
238 if (convert_args("updatev", args) == -1)
239 return NULL;
240
241 if ((data = rrd_update_v(rrdtool_argc, rrdtool_argv)) == NULL) {
242 PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
243 rrd_clear_error();
244 ret = NULL;
245 } else {
246 ret = _rrdtool_util_info2dict(data);
247 rrd_info_free(data);
248 }
249
250 destroy_args();
251 return ret;
252}
253
618986c4
CJ
254static char _rrdtool_fetch__doc__[] = "Fetch data from an RRD.\n\n\
255 Usage: fetch(args..)\n\
256 Arguments:\n\n\
257 filename\n\
258 CF\n\
259 [--resolution|-r resolution]\n\
260 [--start|-s start]\n\
261 [--end|-e end]\n\n\
262 Full documentation can be found at:\n\
263 http://oss.oetiker.ch/rrdtool/doc/rrdfetch.en.html";
264
265static PyObject *
266_rrdtool_fetch(PyObject *self, PyObject *args)
267{
268 PyObject *ret, *range_tup, *dsnam_tup, *data_list, *t;
269 rrd_value_t *data, *datai, dv;
270 unsigned long step, ds_cnt, i, j, row;
271 time_t start, end;
272 char **ds_namv;
273
274 if (convert_args("fetch", args) == -1)
275 return NULL;
276
277 if (rrd_fetch(rrdtool_argc, rrdtool_argv, &start, &end, &step, &ds_cnt,
278 &ds_namv, &data) == -1) {
279 PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
280 rrd_clear_error();
281 ret = NULL;
282 } else {
283 row = (end - start) / step;
284 ret = PyTuple_New(3);
285 range_tup = PyTuple_New(3);
286 dsnam_tup = PyTuple_New(ds_cnt);
287 data_list = PyList_New(row);
288
289 PyTuple_SET_ITEM(ret, 0, range_tup);
290 PyTuple_SET_ITEM(ret, 1, dsnam_tup);
291 PyTuple_SET_ITEM(ret, 2, data_list);
292
293 datai = data;
294
295 PyTuple_SET_ITEM(range_tup, 0, PyLong_FromLong((long)start));
296 PyTuple_SET_ITEM(range_tup, 1, PyLong_FromLong((long)end));
297 PyTuple_SET_ITEM(range_tup, 2, PyLong_FromLong((long)step));
298
299 for (i = 0; i < ds_cnt; i++)
300 PyTuple_SET_ITEM(dsnam_tup, i, PyUnicode_FromString(ds_namv[i]));
301
302 for (i = 0; i < row; i++) {
303 t = PyTuple_New(ds_cnt);
304 PyList_SET_ITEM(data_list, i, t);
305
306 for (j = 0; j < ds_cnt; j++) {
307 dv = *(datai++);
308 if (isnan(dv)) {
309 PyTuple_SET_ITEM(t, j, Py_None);
310 Py_INCREF(Py_None);
311 } else
312 PyTuple_SET_ITEM(t, j, PyFloat_FromDouble((double)dv));
313 }
314 }
315
316 for (i = 0; i < ds_cnt; i++)
317 rrd_freemem(ds_namv[i]);
318
319 rrd_freemem(ds_namv);
320 rrd_freemem(data);
321 }
322
323 destroy_args();
324 return ret;
325}
326
06fe4e5b
CJ
327static char _rrdtool_flushcached__doc__[] = "Flush RRD files from memory.\n\n\
328 Usage: flushcached(args..)\n\
329 Arguments:\n\n\
330 [--daemon address]\n\
331 filename\n\
332 [filename ...]\n\n\
333 Full documentation can be found at:\n\
334 http://oss.oetiker.ch/rrdtool/doc/rrdflushcached.en.html";
335
336static PyObject *
337_rrdtool_flushcached(PyObject *self, PyObject *args)
338{
339 PyObject *ret;
340
341 if (convert_args("flushcached", args) == -1)
342 return NULL;
343
344 if (rrd_flushcached(rrdtool_argc, rrdtool_argv) != 0) {
345 PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
346 rrd_clear_error();
347 ret = NULL;
348 } else {
349 Py_INCREF(Py_None);
350 ret = Py_None;
351 }
352
353 destroy_args();
354 return ret;
355}
356
618986c4
CJ
357static char _rrdtool_graph__doc__[] = "Create a graph based on one or more " \
358 "RRDs.\n\n\
359 Usage: graph(args..)\n\
360 Arguments:\n\n\
361 filename | -\n\
362 [-s|--start start]\n\
363 [-e|--end end]\n\
364 [-S|--step step]\n\
365 [-t|--title string]\n\
366 [-v|--vertical-label string]\n\
367 [-w|--width pixels]\n\
368 [-h|--height pixels]\n\
369 [-j|--only-graph]\n\
370 [-D|--full-size-mode]\n\
371 [-u|--upper-limit value]\n\
372 [-l|--lower-limit value]\n\
373 [-r|--rigid]\n\
374 [-A|--alt-autoscale]\n\
375 [-J|--alt-autoscale-min]\n\
376 [-M|--alt-autoscale-max]\n\
377 [-N|--no-gridfit]\n\
378 [-x|--x-grid (GTM:GST:MTM:MST:LTM:LST:LPR:LFM|none)]\n\
379 [-y|--y-grid (grid step:label factor|none)]\n\
380 [-Y|--alt-y-grid]\n\
381 [-o|--logarithmic]\n\
382 [-X|--units-exponent value]\n\
383 [-L|--units-length value]\n\
384 [--units=si]\n\
385 [--right-axis scale:shift]\n\
386 [--right-axis-label label]\n\
387 [--right-axis-format format-string]\n\
388 [-g|--no-legend]\n\
389 [-F|--force-rules-legend]\n\
390 [--legend-position=(north|south|west|east)]\n\
391 [--legend-direction=(topdown|bottomup)]\n\
392 [-z|--lazy]\n\
393 [--daemon address]\n\
394 [-f|--imginfo printfstr]\n\
395 [-c|--color COLORTAG#rrggbb[aa]]\n\
396 [--grid-dash on:off]\n\
397 [--border width]\n\
398 [--dynamic-labels]\n\
399 [-m|--zoom factor]\n\
400 [-n|--font FONTTAG:size:[font]]\n\
401 [-R|--font-render-mode {normal,light,mono}]\n\
402 [-B|--font-smoothing-threshold size]\n\
403 [-P|--pango-markup]\n\
404 [-G|--graph-render-mode {normal,mono}]\n\
405 [-E|--slope-mode]\n\
406 [-a|--imgformat {PNG,SVG,EPS,PDF}]\n\
407 [-T|--tabwidth value]\n\
408 [-b|--base value]\n\
409 [-W|--watermark string]\n\
410 DEF:vname=rrdfile:ds-name:CF[:step=step][:start=time][:end=time]\n\
411 CDEF:vname=RPN expression\n\
412 VDEF=vname:RPN expression\n\n\
413 Full documentation can be found at:\n\
414 http://oss.oetiker.ch/rrdtool/doc/rrdgraph.en.html";
415
416static PyObject *
417_rrdtool_graph(PyObject *self, PyObject *args, PyObject *kwargs)
418{
419 PyObject *ret;
06fe4e5b 420 int xsize, ysize, i;
618986c4
CJ
421 int keep_in_mem = 0;
422 double ymin, ymax;
06fe4e5b
CJ
423 char **calcpr, *bp;
424#ifdef _POSIX_C_SOURCE
425 FILE *orig_stdout = stdout;
426 size_t bsize;
427#endif
618986c4
CJ
428
429 if (convert_args("graph", args) == -1)
430 return NULL;
431
06fe4e5b
CJ
432 if (rrdtool_argc >= 2 && strcmp(rrdtool_argv[1], "-") == 0) {
433#ifdef _POSIX_C_SOURCE
618986c4 434 keep_in_mem = 1;
06fe4e5b
CJ
435#else
436 PyErr_SetString(rrdtool_ProgrammingError,
437 "Output filename cannot be '-', because this platform does not "\
438 "support output buffering");
439 destroy_args();
440 return NULL;
441#endif
618986c4
CJ
442 }
443
06fe4e5b
CJ
444#ifdef _POSIX_C_SOURCE
445 if (keep_in_mem)
446 stdout = open_memstream(&bp, &bsize);
447#endif
448
618986c4
CJ
449 if (rrd_graph(rrdtool_argc, rrdtool_argv, &calcpr, &xsize, &ysize, NULL,
450 &ymin, &ymax) == -1) {
451 PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
452 rrd_clear_error();
453 ret = NULL;
454 } else {
455 ret = PyTuple_New(keep_in_mem ? 4 : 3);
456
457 PyTuple_SET_ITEM(ret, 0, PyLong_FromLong((long)xsize));
458 PyTuple_SET_ITEM(ret, 1, PyLong_FromLong((long)ysize));
459
460 if (calcpr) {
461 PyObject *e, *t;
462
463 e = PyList_New(0);
464 PyTuple_SET_ITEM(ret, 2, e);
465
466 for (i = 0; calcpr[i]; i++) {
467 t = PyUnicode_FromString(calcpr[i]);
468 PyList_Append(e, t);
469 Py_DECREF(t);
470 rrd_freemem(calcpr[i]);
471 }
472 } else {
473 Py_INCREF(Py_None);
474 PyTuple_SET_ITEM(ret, 2, Py_None);
475 }
476
477 /* feed buffered contents into a PyBytes object */
478 if (keep_in_mem) {
479 PyObject *pb;
06fe4e5b
CJ
480
481 fflush(stdout);
482 pb = PyBytes_FromStringAndSize(bp, bsize);
618986c4
CJ
483
484 PyTuple_SET_ITEM(ret, 3, pb);
485 }
486 }
487
06fe4e5b
CJ
488 if (keep_in_mem) {
489 fclose(stdout);
490 stdout = orig_stdout;
491 }
492
493 destroy_args();
494 return ret;
495}
496
497static char _rrdtool_graphv__doc__[] = "Create a graph based on one or more "\
498 "RRDs and return header information.\n\n\
499 This function works in the same manner as 'graph', but will return a info\n\
500 dictionary instead of None.";
501
502static PyObject *
503_rrdtool_graphv(PyObject *self, PyObject *args)
504{
505 PyObject *ret;
506 rrd_info_t *data;
507
508 if (convert_args("graphv", args) == -1)
509 return NULL;
510
511 if ((data = rrd_graph_v(rrdtool_argc, rrdtool_argv)) == NULL) {
512 PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
513 rrd_clear_error();
514 ret = NULL;
515 } else {
516 ret = _rrdtool_util_info2dict(data);
517 rrd_info_free(data);
518 }
519
520 destroy_args();
521 return ret;
522}
523
524static char _rrdtool_tune__doc__[] = "Modify some basic properties of a " \
525 "Round Robin Database.\n\n\
526 Usage: tune(args..)\n\
527 Arguments:\n\n\
528 filename\n\
529 [-h|--heartbeat ds-name:heartbeat]\n\
530 [-i|--minimum ds-name:min]\n\
531 [-a|--maximum ds-name:max]\n\
532 [-d|--data-source-type ds-name:DST]\n\
533 [-r|--data-source-rename old-name:new-name]\n\n\
534 Full documentation can be found at:\n\
535 http://oss.oetiker.ch/rrdtool/doc/rrdtune.en.html";
536
537static PyObject *
538_rrdtool_tune(PyObject *self, PyObject *args)
539{
540 PyObject *ret;
541
542 if (convert_args("tune", args) == -1)
543 return NULL;
544
545 if (rrd_tune(rrdtool_argc, rrdtool_argv) == -1) {
546 PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
547 rrd_clear_error();
548 ret = NULL;
549 } else {
550 Py_INCREF(Py_None);
551 ret = Py_None;
552 }
553
554 destroy_args();
555 return ret;
556}
557
558static char _rrdtool_first__doc__[] = "Get the first UNIX timestamp of the "\
559 "first data sample in an Round Robin Database.\n\n\
560 Usage: first(args..)\n\
561 Arguments:\n\n\
562 filename\n\
563 [--rraindex number]\n\n\
564 Full documentation can be found at:\n\
565 http://oss.oetiker.ch/rrdtool/doc/rrdfirst.en.html";
566
567static PyObject *
568_rrdtool_first(PyObject *self, PyObject *args)
569{
570 PyObject *ret;
571 int ts;
572
573 if (convert_args("first", args) == -1)
574 return NULL;
575
576 if ((ts = rrd_first(rrdtool_argc, rrdtool_argv)) == -1) {
577 PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
578 rrd_clear_error();
579 ret = NULL;
580 } else
581 ret = PyLong_FromLong((long)ts);
582
583 destroy_args();
584 return ret;
585}
586
587static char _rrdtool_last__doc__[] = "Get the UNIX timestamp of the most "\
588 "recent data sample in an Round Robin Database.\n\n\
589 Usage: last(args..)\n\
590 Arguments:\n\n\
591 filename\n\
592 [--daemon address]\n\n\
593 Full documentation can be found at:\n\
594 http://oss.oetiker.ch/rrdtool/doc/rrdlast.en.html";
595
596static PyObject *
597_rrdtool_last(PyObject *self, PyObject *args)
598{
599 PyObject *ret;
600 int ts;
601
602 if (convert_args("last", args) == -1)
603 return NULL;
604
605 if ((ts = rrd_last(rrdtool_argc, rrdtool_argv)) == -1) {
606 PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
607 rrd_clear_error();
608 ret = NULL;
609 } else
610 ret = PyLong_FromLong((long)ts);
611
612 destroy_args();
613 return ret;
614}
615
616static char _rrdtool_resize__doc__[] = "Modify the number of rows in a "\
617 "Round Robin Database.\n\n\
618 Usage: resize(args..)\n\
619 Arguments:\n\n\
620 filename\n\
621 rra-num\n\
622 GROW|SHRINK\n\
623 rows\n\n\
624 Full documentation can be found at:\n\
625 http://oss.oetiker.ch/rrdtool/doc/rrdlast.en.html";
626
627static PyObject *
628_rrdtool_resize(PyObject *self, PyObject *args)
629{
630 PyObject *ret;
631 int ts;
632
633 if (convert_args("resize", args) == -1)
634 return NULL;
635
636 if ((ts = rrd_resize(rrdtool_argc, rrdtool_argv)) == -1) {
637 PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
638 rrd_clear_error();
639 ret = NULL;
640 } else {
641 Py_INCREF(Py_None);
642 ret = Py_None;
643 }
644
645 destroy_args();
646 return ret;
647}
648
649static char _rrdtool_info__doc__[] = "Extract header information from an "\
650 "Round Robin Database.\n\n\
651 Usage: info(filename)\n\
652 Arguments:\n\n\
653 filename\n\n\
654 Full documentation can be found at:\n\
655 http://oss.oetiker.ch/rrdtool/doc/rrdinfo.en.html";
656
657static PyObject *
658_rrdtool_info(PyObject *self, PyObject *args)
659{
660 PyObject *ret;
661 rrd_info_t *data;
662
663 if (convert_args("info", args) == -1)
664 return NULL;
665
666 if ((data = rrd_info(rrdtool_argc, rrdtool_argv)) == NULL) {
667 PyErr_SetString(rrdtool_OperationalError, rrd_get_error());
668 rrd_clear_error();
669 ret = NULL;
670 } else {
671 ret = _rrdtool_util_info2dict(data);
672 rrd_info_free(data);
673 }
ce6564c2 674
618986c4
CJ
675 destroy_args();
676 return ret;
ce6564c2
CJ
677}
678
b4570f2a
CJ
679static char _rrdtool_lib_version__doc__[] = "Get the version this binding "\
680 "was compiled against.";
681
682static PyObject *
683_rrdtool_lib_version(PyObject *self, PyObject *args)
684{
685 return PyUnicode_FromString(rrd_strversion());
686}
687
ce6564c2 688static PyMethodDef rrdtool_methods[] = {
618986c4
CJ
689 {"create", (PyCFunction)_rrdtool_create,
690 METH_VARARGS, _rrdtool_create__doc__},
691 {"update", (PyCFunction)_rrdtool_update,
692 METH_VARARGS, _rrdtool_update__doc__},
06fe4e5b
CJ
693 {"updatev", (PyCFunction)_rrdtool_updatev,
694 METH_VARARGS, _rrdtool_updatev__doc__},
618986c4
CJ
695 {"fetch", (PyCFunction)_rrdtool_fetch,
696 METH_VARARGS, _rrdtool_fetch__doc__},
06fe4e5b
CJ
697 {"flushcached", (PyCFunction)_rrdtool_flushcached,
698 METH_VARARGS, _rrdtool_flushcached__doc__},
618986c4 699 {"graph", (PyCFunction)_rrdtool_graph,
06fe4e5b
CJ
700 METH_VARARGS, _rrdtool_graph__doc__},
701 {"graphv", (PyCFunction)_rrdtool_graphv,
702 METH_VARARGS, _rrdtool_graphv__doc__},
703 {"tune", (PyCFunction)_rrdtool_tune,
704 METH_VARARGS, _rrdtool_tune__doc__},
705 {"first", (PyCFunction)_rrdtool_first,
706 METH_VARARGS, _rrdtool_first__doc__},
707 {"last", (PyCFunction)_rrdtool_last,
708 METH_VARARGS, _rrdtool_last__doc__},
709 {"resize", (PyCFunction)_rrdtool_resize,
710 METH_VARARGS, _rrdtool_resize__doc__},
711 {"info", (PyCFunction)_rrdtool_info,
712 METH_VARARGS, _rrdtool_info__doc__},
b4570f2a
CJ
713 {"lib_version", (PyCFunction)_rrdtool_lib_version,
714 METH_VARARGS, _rrdtool_lib_version__doc__},
ce6564c2
CJ
715 {NULL, NULL, 0, NULL}
716};
717
718static struct PyModuleDef rrdtoolmodule = {
719 PyModuleDef_HEAD_INIT,
720 "rrdtool",
721 "rrdtool bindings for Python 3",
722 -1,
723 rrdtool_methods
724};
725
726PyMODINIT_FUNC
727PyInit_rrdtool(void)
728{
729 PyObject *m;
730
731 m = PyModule_Create(&rrdtoolmodule);
732 if (m == NULL)
733 return NULL;
734
735 rrdtool_ProgrammingError = PyErr_NewException("rrdtool.ProgrammingError",
736 NULL, NULL);
737 Py_INCREF(rrdtool_ProgrammingError);
738 PyModule_AddObject(m, "ProgrammingError", rrdtool_ProgrammingError);
739
740 rrdtool_OperationalError = PyErr_NewException("rrdtool.OperationalError",
741 NULL, NULL);
742 Py_INCREF(rrdtool_OperationalError);
743 PyModule_AddObject(m, "OperationalError", rrdtool_OperationalError);
06fe4e5b 744 PyModule_AddObject(m, "__version__", PyUnicode_FromString(_version));
ce6564c2
CJ
745
746 return m;
747}