1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * clippy (CLI preparator in python) wrapper for FRR command_graph
4 * Copyright (C) 2016-2017 David Lamparter for NetDEF, Inc.
7 /* note: this wrapper is intended to be used as build-time helper. while
8 * it should be generally correct and proper, there may be the occasional
9 * memory leak or SEGV for things that haven't been well-tested.
12 /* This file is "exempt" from having
14 * as the first include statement because Python.h also does environment
15 * setup & these trample over each other.
22 #include "structmember.h"
26 #include "command_graph.h"
30 static PyObject
*graph_to_pyobj(struct wrap_graph
*graph
,
31 struct graph_node
*gn
);
34 * nodes are wrapped as follows:
35 * - instances can only be acquired from a graph
36 * - the same node will return the same wrapper object (they're buffered
38 * - a reference is held onto the graph
39 * - fields are copied for easy access with PyMemberDef
41 struct wrap_graph_node
{
54 struct graph_node
*node
;
55 struct wrap_graph
*wgraph
;
60 * graphs are wrapped as follows:
61 * - they can only be created by parsing a definition string
62 * - there's a table here for the wrapped nodes (nodewrappers), indexed
63 * by "idx" (corresponds to node's position in graph's table of nodes)
64 * - graphs do NOT hold references to nodes (would be circular)
71 struct wrap_graph_node
**nodewrappers
;
74 static PyObject
*refuse_new(PyTypeObject
*type
, PyObject
*args
, PyObject
*kwds
)
76 PyErr_SetString(PyExc_ValueError
,
77 "cannot create instances of this type");
81 #define member(name, type) \
83 (char *)#name, type, offsetof(struct wrap_graph_node, name), \
84 READONLY, (char *)#name " (" #type ")" \
86 static PyMemberDef members_graph_node
[] = {
87 member(allowrepeat
, T_BOOL
), member(type
, T_STRING
),
88 member(deprecated
, T_BOOL
), member(hidden
, T_BOOL
),
89 member(text
, T_STRING
), member(desc
, T_STRING
),
90 member(min
, T_LONGLONG
), member(max
, T_LONGLONG
),
91 member(varname
, T_STRING
), {},
96 * node.next() -- returns list of all "next" nodes.
97 * this will include circles if the graph has them.
99 static PyObject
*graph_node_next(PyObject
*self
, PyObject
*args
)
101 struct wrap_graph_node
*wrap
= (struct wrap_graph_node
*)self
;
105 && ((struct cmd_token
*)wrap
->node
->data
)->type
== END_TKN
)
106 return PyList_New(0);
107 pylist
= PyList_New(vector_active(wrap
->node
->to
));
108 for (size_t i
= 0; i
< vector_active(wrap
->node
->to
); i
++) {
109 struct graph_node
*gn
= vector_slot(wrap
->node
->to
, i
);
110 PyList_SetItem(pylist
, i
, graph_to_pyobj(wrap
->wgraph
, gn
));
116 * node.join() -- return FORK's JOIN node or None
118 static PyObject
*graph_node_join(PyObject
*self
, PyObject
*args
)
120 struct wrap_graph_node
*wrap
= (struct wrap_graph_node
*)self
;
122 if (!wrap
->node
->data
123 || ((struct cmd_token
*)wrap
->node
->data
)->type
== END_TKN
)
126 struct cmd_token
*tok
= wrap
->node
->data
;
127 if (tok
->type
!= FORK_TKN
)
130 return graph_to_pyobj(wrap
->wgraph
, tok
->forkjoin
);
133 static PyMethodDef methods_graph_node
[] = {
134 {"next", graph_node_next
, METH_NOARGS
, "outbound graph edge list"},
135 {"join", graph_node_join
, METH_NOARGS
, "outbound join node"},
138 static void graph_node_wrap_free(void *arg
)
140 struct wrap_graph_node
*wrap
= arg
;
141 wrap
->wgraph
->nodewrappers
[wrap
->idx
] = NULL
;
142 Py_DECREF(wrap
->wgraph
);
145 static PyTypeObject typeobj_graph_node
= {
146 PyVarObject_HEAD_INIT(NULL
, 0).tp_name
= "_clippy.GraphNode",
147 .tp_basicsize
= sizeof(struct wrap_graph_node
),
148 .tp_flags
= Py_TPFLAGS_DEFAULT
,
149 .tp_doc
= "struct graph_node *",
150 .tp_new
= refuse_new
,
151 .tp_free
= graph_node_wrap_free
,
152 .tp_members
= members_graph_node
,
153 .tp_methods
= methods_graph_node
,
156 static PyObject
*graph_to_pyobj(struct wrap_graph
*wgraph
,
157 struct graph_node
*gn
)
159 struct wrap_graph_node
*wrap
;
162 for (i
= 0; i
< vector_active(wgraph
->graph
->nodes
); i
++)
163 if (vector_slot(wgraph
->graph
->nodes
, i
) == gn
)
165 if (i
== vector_active(wgraph
->graph
->nodes
)) {
166 PyErr_SetString(PyExc_ValueError
, "cannot find node in graph");
169 if (wgraph
->nodewrappers
[i
]) {
170 PyObject
*obj
= (PyObject
*)wgraph
->nodewrappers
[i
];
175 wrap
= (struct wrap_graph_node
*)typeobj_graph_node
.tp_alloc(
176 &typeobj_graph_node
, 0);
179 wgraph
->nodewrappers
[i
] = wrap
;
183 wrap
->wgraph
= wgraph
;
186 wrap
->allowrepeat
= false;
188 struct cmd_token
*tok
= gn
->data
;
193 break /* no semicolon */
195 item(WORD_TKN
); // words
196 item(VARIABLE_TKN
); // almost anything
197 item(RANGE_TKN
); // integer range
198 item(IPV4_TKN
); // IPV4 addresses
199 item(IPV4_PREFIX_TKN
); // IPV4 network prefixes
200 item(IPV6_TKN
); // IPV6 prefixes
201 item(IPV6_PREFIX_TKN
); // IPV6 network prefixes
202 item(MAC_TKN
); // MAC address
203 item(MAC_PREFIX_TKN
); // MAC address with mask
204 item(ASNUM_TKN
); // ASNUM
217 wrap
->deprecated
= !!(tok
->attr
& CMD_ATTR_DEPRECATED
);
218 wrap
->hidden
= !!(tok
->attr
& CMD_ATTR_HIDDEN
);
219 wrap
->text
= tok
->text
;
220 wrap
->desc
= tok
->desc
;
221 wrap
->varname
= tok
->varname
;
222 wrap
->min
= tok
->min
;
223 wrap
->max
= tok
->max
;
224 wrap
->allowrepeat
= tok
->allowrepeat
;
227 return (PyObject
*)wrap
;
230 #define member(name, type) \
232 (char *)#name, type, offsetof(struct wrap_graph, name), \
233 READONLY, (char *)#name " (" #type ")" \
235 static PyMemberDef members_graph
[] = {
236 member(definition
, T_STRING
),
241 /* graph.first() - root node */
242 static PyObject
*graph_first(PyObject
*self
, PyObject
*args
)
244 struct wrap_graph
*gwrap
= (struct wrap_graph
*)self
;
245 struct graph_node
*gn
= vector_slot(gwrap
->graph
->nodes
, 0);
246 return graph_to_pyobj(gwrap
, gn
);
249 static PyMethodDef methods_graph
[] = {
250 {"first", graph_first
, METH_NOARGS
, "first graph node"},
253 static PyObject
*graph_parse(PyTypeObject
*type
, PyObject
*args
,
256 static void graph_wrap_free(void *arg
)
258 struct wrap_graph
*wgraph
= arg
;
260 graph_delete_graph(wgraph
->graph
);
261 free(wgraph
->nodewrappers
);
262 free(wgraph
->definition
);
265 static PyTypeObject typeobj_graph
= {
266 PyVarObject_HEAD_INIT(NULL
, 0).tp_name
= "_clippy.Graph",
267 .tp_basicsize
= sizeof(struct wrap_graph
),
268 .tp_flags
= Py_TPFLAGS_DEFAULT
,
269 .tp_doc
= "struct graph *",
270 .tp_new
= graph_parse
,
271 .tp_free
= graph_wrap_free
,
272 .tp_members
= members_graph
,
273 .tp_methods
= methods_graph
,
276 /* top call / entrypoint for python code */
277 static PyObject
*graph_parse(PyTypeObject
*type
, PyObject
*args
, PyObject
*kwds
)
279 const char *def
, *doc
= NULL
;
280 struct wrap_graph
*gwrap
;
281 static const char *kwnames
[] = {"cmddef", "doc", NULL
};
283 gwrap
= (struct wrap_graph
*)typeobj_graph
.tp_alloc(&typeobj_graph
, 0);
287 if (!PyArg_ParseTupleAndKeywords(args
, kwds
, "s|s", (char **)kwnames
,
291 struct graph
*graph
= graph_new();
292 struct cmd_token
*token
= cmd_token_new(START_TKN
, 0, NULL
, NULL
);
293 graph_new_node(graph
, token
, (void (*)(void *)) & cmd_token_del
);
295 struct cmd_element cmd
= {.string
= def
, .doc
= doc
};
296 cmd_graph_parse(graph
, &cmd
);
297 cmd_graph_names(graph
);
299 gwrap
->graph
= graph
;
300 gwrap
->definition
= strdup(def
);
301 gwrap
->nodewrappers
= calloc(vector_active(graph
->nodes
),
302 sizeof(gwrap
->nodewrappers
[0]));
303 return (PyObject
*)gwrap
;
306 static PyMethodDef clippy_methods
[] = {
307 {"parse", clippy_parse
, METH_VARARGS
, "Parse a C file"},
308 {NULL
, NULL
, 0, NULL
}};
310 #if PY_MAJOR_VERSION >= 3
311 static struct PyModuleDef pymoddef_clippy
= {
312 PyModuleDef_HEAD_INIT
,
314 NULL
, /* docstring */
318 #define modcreate() PyModule_Create(&pymoddef_clippy)
319 #define initret(val) return val;
321 #define modcreate() Py_InitModule("_clippy", clippy_methods)
322 #define initret(val) \
325 Py_FatalError("initialization failure"); \
330 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
331 PyMODINIT_FUNC
command_py_init(void)
335 if (PyType_Ready(&typeobj_graph_node
) < 0)
337 if (PyType_Ready(&typeobj_graph
) < 0)
344 if (PyModule_AddIntMacro(pymod
, CMD_ATTR_YANG
)
345 || PyModule_AddIntMacro(pymod
, CMD_ATTR_HIDDEN
)
346 || PyModule_AddIntMacro(pymod
, CMD_ATTR_DEPRECATED
)
347 || PyModule_AddIntMacro(pymod
, CMD_ATTR_NOSH
))
350 Py_INCREF(&typeobj_graph_node
);
351 PyModule_AddObject(pymod
, "GraphNode", (PyObject
*)&typeobj_graph_node
);
352 Py_INCREF(&typeobj_graph
);
353 PyModule_AddObject(pymod
, "Graph", (PyObject
*)&typeobj_graph
);
354 if (!elf_py_init(pymod
))