]> sigrok.org Git - libsigrok.git/blame - bindings/python/sigrok/core/classes.i
bindings/python: Handle import failures without crashing
[libsigrok.git] / bindings / python / sigrok / core / classes.i
CommitLineData
f7740954
ML
1/*
2 * This file is part of the libsigrok project.
3 *
4 * Copyright (C) 2014 Martin Ling <martin-sigrok@earth.li>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
6a8c1d68
ML
20%define DOCSTRING
21"@mainpage API Reference
22
23Introduction
24------------
25
26The pysigrok API provides an object-oriented Python interface to the
52ff4f6a 27functionality in libsigrok. It is built on top of the libsigrokcxx C++ API.
6a8c1d68
ML
28
29Getting started
30---------------
31
32Usage of the pysigrok API needs to begin with a call to Context.create().
33This will create the global libsigrok context and returns a Context object.
34Methods on this object provide access to the hardware drivers, input and output
35formats supported by the library, as well as means of creating other objects
36such as sessions and triggers.
37
38Error handling
39--------------
40
41When any libsigrok C API call returns an error, an Error exception is raised,
42which provides access to the error code and description."
43%enddef
44
45%module(docstring=DOCSTRING) classes
f7740954
ML
46
47%{
aae2273b 48#include <stdio.h>
f7740954 49#include <pygobject.h>
ed6b4c47 50#include <numpy/arrayobject.h>
f7740954 51
aae2273b 52PyObject *PyGObject_lib;
f7740954
ML
53PyObject *GLib;
54PyTypeObject *IOChannel;
55PyTypeObject *PollFD;
56
0e1a7fe9 57#include "config.h"
abc7146d
ML
58
59#if PYGOBJECT_FLAGS_SIGNED
60typedef gint pyg_flags_type;
61#else
62typedef guint pyg_flags_type;
63#endif
64
f7740954
ML
65%}
66
67%init %{
aae2273b
AG
68 PyGObject_lib = pygobject_init(-1, -1, -1);
69 if (!PyGObject_lib)
70 fprintf(stderr, "pygobject initialization failed.\n");
f7740954 71 GLib = PyImport_ImportModule("gi.repository.GLib");
aae2273b
AG
72 /*
73 * This check can't save us if the import fails, but at least it gives us
74 * a starting point to trace the issue versus straight out crashing.
75 */
76 if (!GLib) {
77 fprintf(stderr, "Import of gi.repository.GLib failed.\n");
78 return;
79 }
f7740954
ML
80 IOChannel = (PyTypeObject *) PyObject_GetAttrString(GLib, "IOChannel");
81 PollFD = (PyTypeObject *) PyObject_GetAttrString(GLib, "PollFD");
ed6b4c47 82 import_array();
f7740954
ML
83%}
84
85/* Map file objects to file descriptors. */
86%typecheck(SWIG_TYPECHECK_POINTER) int fd {
87 $1 = (PyObject_AsFileDescriptor($input) != -1);
88}
89
90%typemap(in) int fd {
91 int fd = PyObject_AsFileDescriptor($input);
92 if (fd == -1)
93 SWIG_exception(SWIG_TypeError,
94 "Expected file object or integer file descriptor");
95 else
96 $1 = fd;
97}
98
99/* Map from Glib::Variant to native Python types. */
100%typemap(out) Glib::VariantBase {
101 GValue *value = g_new0(GValue, 1);
102 g_value_init(value, G_TYPE_VARIANT);
103 g_value_set_variant(value, $1.gobj());
104 PyObject *variant = pyg_value_as_pyobject(value, true);
105 $result = PyObject_CallMethod(variant,
106 const_cast<char *>("unpack"),
107 const_cast<char *>(""), NULL);
108 Py_XDECREF(variant);
109 g_free(value);
110}
111
112/* Map from Glib::IOCondition to GLib.IOCondition. */
113%typecheck(SWIG_TYPECHECK_POINTER) Glib::IOCondition {
abc7146d 114 pyg_flags_type flags;
f7740954
ML
115 $1 = pygobject_check($input, &PyGFlags_Type) &&
116 (pyg_flags_get_value(G_TYPE_IO_CONDITION, $input, &flags) != -1);
117}
118
119%typemap(in) Glib::IOCondition {
120 if (!pygobject_check($input, &PyGFlags_Type))
121 SWIG_exception(SWIG_TypeError, "Expected GLib.IOCondition value");
abc7146d 122 pyg_flags_type flags;
f7740954
ML
123 if (pyg_flags_get_value(G_TYPE_IO_CONDITION, $input, &flags) == -1)
124 SWIG_exception(SWIG_TypeError, "Not a valid Glib.IOCondition value");
125 $1 = (Glib::IOCondition) flags;
126}
127
128/* And back */
129%typemap(out) Glib::IOCondition {
130 GValue *value = g_new0(GValue, 1);
131 g_value_init(value, G_TYPE_IO_CONDITION);
132 g_value_set_flags(value, &$1);
133 $result = pyg_value_as_pyobject(value, true);
134 g_free(value);
135}
136
137/* Map from GLib.PollFD to Glib::PollFD *. */
138%typecheck(SWIG_TYPECHECK_POINTER) Glib::PollFD {
139 $1 = pygobject_check($input, PollFD);
140}
141
142%typemap(in) Glib::PollFD {
143 if (!pygobject_check($input, PollFD))
144 SWIG_exception(SWIG_TypeError, "Expected GLib.PollFD");
145 PyObject *fd_obj = PyObject_GetAttrString($input, "fd");
146 PyObject *events_obj = PyObject_GetAttrString($input, "events");
abc7146d 147 pyg_flags_type flags;
f7740954
ML
148 pyg_flags_get_value(G_TYPE_IO_CONDITION, events_obj, &flags);
149 int fd = PyInt_AsLong(fd_obj);
150 Glib::IOCondition events = (Glib::IOCondition) flags;
151 $1 = Glib::PollFD(fd, events);
152}
153
154/* Map from GLib.IOChannel to Glib::IOChannel *. */
155%typecheck(SWIG_TYPECHECK_POINTER) Glib::RefPtr<Glib::IOChannel> {
156 $1 = pygobject_check($input, IOChannel);
157}
158
159%typemap(in) Glib::RefPtr<Glib::IOChannel> {
160 if (!pygobject_check($input, IOChannel))
161 SWIG_exception(SWIG_TypeError, "Expected GLib.IOChannel");
162 $1 = Glib::wrap((GIOChannel *) PyObject_Hash($input), true);
163}
164
165/* Map from callable PyObject to SourceCallbackFunction. */
166%typecheck(SWIG_TYPECHECK_POINTER) sigrok::SourceCallbackFunction {
167 $1 = PyCallable_Check($input);
168}
169
170%typemap(in) sigrok::SourceCallbackFunction {
171 if (!PyCallable_Check($input))
172 SWIG_exception(SWIG_TypeError, "Expected a callable Python object");
173
174 $1 = [=] (Glib::IOCondition revents) {
175 auto gstate = PyGILState_Ensure();
176
177 GValue *value = g_new0(GValue, 1);
178 g_value_init(value, G_TYPE_IO_CONDITION);
179 g_value_set_flags(value, revents);
180 auto revents_obj = pyg_value_as_pyobject(value, true);
181 g_free(value);
182
183 auto arglist = Py_BuildValue("(O)", revents_obj);
184
185 auto result = PyEval_CallObject($input, arglist);
186
187 Py_XDECREF(arglist);
188 Py_XDECREF(revents_obj);
189
75fb3036
ML
190 bool completed = !PyErr_Occurred();
191
192 if (!completed)
193 PyErr_Print();
194
195 bool valid_result = (completed && PyBool_Check(result));
f7740954 196
75fb3036
ML
197 if (completed && !valid_result)
198 {
199 PyErr_SetString(PyExc_TypeError,
200 "EventSource callback did not return a boolean");
201 PyErr_Print();
202 }
203
204 bool retval = (valid_result && result == Py_True);
f7740954
ML
205
206 Py_XDECREF(result);
207
208 PyGILState_Release(gstate);
209
75fb3036
ML
210 if (!valid_result)
211 throw sigrok::Error(SR_ERR);
212
f7740954
ML
213 return retval;
214 };
215
216 Py_XINCREF($input);
217}
218
219/* Map from callable PyObject to LogCallbackFunction */
220%typecheck(SWIG_TYPECHECK_POINTER) sigrok::LogCallbackFunction {
221 $1 = PyCallable_Check($input);
222}
223
224%typemap(in) sigrok::LogCallbackFunction {
225 if (!PyCallable_Check($input))
226 SWIG_exception(SWIG_TypeError, "Expected a callable Python object");
227
682fb08c 228 $1 = [=] (const sigrok::LogLevel *loglevel, std::string message) {
f7740954
ML
229 auto gstate = PyGILState_Ensure();
230
231 auto log_obj = SWIG_NewPointerObj(
232 SWIG_as_voidptr(loglevel), SWIGTYPE_p_sigrok__LogLevel, 0);
233
234 auto string_obj = PyString_FromString(message.c_str());
235
236 auto arglist = Py_BuildValue("(OO)", log_obj, string_obj);
237
238 auto result = PyEval_CallObject($input, arglist);
239
240 Py_XDECREF(arglist);
241 Py_XDECREF(log_obj);
242 Py_XDECREF(string_obj);
75fb3036
ML
243
244 bool completed = !PyErr_Occurred();
245
246 if (!completed)
247 PyErr_Print();
248
249 bool valid_result = (completed && result == Py_None);
250
f7740954
ML
251 Py_XDECREF(result);
252
75fb3036
ML
253 if (completed && !valid_result)
254 {
255 PyErr_SetString(PyExc_TypeError,
256 "Log callback did not return None");
257 PyErr_Print();
258 }
259
f7740954 260 PyGILState_Release(gstate);
75fb3036
ML
261
262 if (!valid_result)
263 throw sigrok::Error(SR_ERR);
f7740954
ML
264 };
265
266 Py_XINCREF($input);
267}
268
269/* Map from callable PyObject to DatafeedCallbackFunction */
270%typecheck(SWIG_TYPECHECK_POINTER) sigrok::DatafeedCallbackFunction {
271 $1 = PyCallable_Check($input);
272}
273
274%typemap(in) sigrok::DatafeedCallbackFunction {
275 if (!PyCallable_Check($input))
276 SWIG_exception(SWIG_TypeError, "Expected a callable Python object");
277
278 $1 = [=] (std::shared_ptr<sigrok::Device> device,
279 std::shared_ptr<sigrok::Packet> packet) {
280 auto gstate = PyGILState_Ensure();
281
282 auto device_obj = SWIG_NewPointerObj(
283 SWIG_as_voidptr(new std::shared_ptr<sigrok::Device>(device)),
284 SWIGTYPE_p_std__shared_ptrT_sigrok__Device_t, SWIG_POINTER_OWN);
285
286 auto packet_obj = SWIG_NewPointerObj(
287 SWIG_as_voidptr(new std::shared_ptr<sigrok::Packet>(packet)),
288 SWIGTYPE_p_std__shared_ptrT_sigrok__Packet_t, SWIG_POINTER_OWN);
289
290 auto arglist = Py_BuildValue("(OO)", device_obj, packet_obj);
291
292 auto result = PyEval_CallObject($input, arglist);
293
294 Py_XDECREF(arglist);
295 Py_XDECREF(device_obj);
296 Py_XDECREF(packet_obj);
75fb3036
ML
297
298 bool completed = !PyErr_Occurred();
299
300 if (!completed)
301 PyErr_Print();
302
303 bool valid_result = (completed && result == Py_None);
304
f7740954
ML
305 Py_XDECREF(result);
306
75fb3036
ML
307 if (completed && !valid_result)
308 {
309 PyErr_SetString(PyExc_TypeError,
310 "Datafeed callback did not return None");
311 PyErr_Print();
312 }
313
f7740954 314 PyGILState_Release(gstate);
75fb3036
ML
315
316 if (!valid_result)
317 throw sigrok::Error(SR_ERR);
f7740954
ML
318 };
319
320 Py_XINCREF($input);
321}
322
ea22dc10
ML
323/* Cast PacketPayload pointers to correct subclass type. */
324%ignore sigrok::Packet::payload;
325
326%extend sigrok::Packet
327{
328 std::shared_ptr<sigrok::Header> _payload_header()
329 {
330 return dynamic_pointer_cast<sigrok::Header>($self->payload());
331 }
332 std::shared_ptr<sigrok::Meta> _payload_meta()
333 {
334 return dynamic_pointer_cast<sigrok::Meta>($self->payload());
335 }
336 std::shared_ptr<sigrok::Analog> _payload_analog()
337 {
338 return dynamic_pointer_cast<sigrok::Analog>($self->payload());
339 }
340 std::shared_ptr<sigrok::Logic> _payload_logic()
341 {
342 return dynamic_pointer_cast<sigrok::Logic>($self->payload());
343 }
344}
345
346%extend sigrok::Packet
347{
348%pythoncode
349{
350 def _payload(self):
351 if self.type == PacketType.HEADER:
352 return self._payload_header()
353 elif self.type == PacketType.META:
354 return self._payload_meta()
355 elif self.type == PacketType.LOGIC:
356 return self._payload_logic()
357 elif self.type == PacketType.ANALOG:
358 return self._payload_analog()
359 else:
360 return None
361
362 payload = property(_payload)
363}
364}
365
f7740954
ML
366%{
367
161dc24d 368#include "libsigrokcxx/libsigrokcxx.hpp"
f7740954
ML
369
370/* Convert from a Python dict to a std::map<std::string, std::string> */
58aa1f83 371std::map<std::string, std::string> dict_to_map_string(PyObject *dict)
f7740954
ML
372{
373 if (!PyDict_Check(dict))
374 throw sigrok::Error(SR_ERR_ARG);
375
376 std::map<std::string, std::string> output;
377
378 PyObject *py_key, *py_value;
379 Py_ssize_t pos = 0;
380
381 while (PyDict_Next(dict, &pos, &py_key, &py_value)) {
382 if (!PyString_Check(py_key))
383 throw sigrok::Error(SR_ERR_ARG);
384 if (!PyString_Check(py_value))
385 throw sigrok::Error(SR_ERR_ARG);
386 auto key = PyString_AsString(py_key);
387 auto value = PyString_AsString(py_value);
388 output[key] = value;
389 }
390
391 return output;
392}
393
394/* Convert from a Python type to Glib::Variant, according to config key data type. */
395Glib::VariantBase python_to_variant_by_key(PyObject *input, const sigrok::ConfigKey *key)
396{
9d229ecb 397 enum sr_datatype type = (enum sr_datatype) key->data_type()->id();
f7740954
ML
398
399 if (type == SR_T_UINT64 && PyInt_Check(input))
400 return Glib::Variant<guint64>::create(PyInt_AsLong(input));
401 if (type == SR_T_UINT64 && PyLong_Check(input))
402 return Glib::Variant<guint64>::create(PyLong_AsLong(input));
403 else if (type == SR_T_STRING && PyString_Check(input))
f90ed2d1 404 return Glib::Variant<Glib::ustring>::create(PyString_AsString(input));
f7740954
ML
405 else if (type == SR_T_BOOL && PyBool_Check(input))
406 return Glib::Variant<bool>::create(input == Py_True);
407 else if (type == SR_T_FLOAT && PyFloat_Check(input))
408 return Glib::Variant<double>::create(PyFloat_AsDouble(input));
409 else if (type == SR_T_INT32 && PyInt_Check(input))
410 return Glib::Variant<gint32>::create(PyInt_AsLong(input));
411 else
412 throw sigrok::Error(SR_ERR_ARG);
413}
414
58aa1f83
ML
415/* Convert from a Python type to Glib::Variant, according to Option data type. */
416Glib::VariantBase python_to_variant_by_option(PyObject *input,
417 std::shared_ptr<sigrok::Option> option)
418{
3b161085 419 GVariantType *type = option->default_value().get_type().gobj();
58aa1f83
ML
420
421 if (type == G_VARIANT_TYPE_UINT64 && PyInt_Check(input))
422 return Glib::Variant<guint64>::create(PyInt_AsLong(input));
423 if (type == G_VARIANT_TYPE_UINT64 && PyLong_Check(input))
424 return Glib::Variant<guint64>::create(PyLong_AsLong(input));
425 else if (type == G_VARIANT_TYPE_STRING && PyString_Check(input))
f90ed2d1 426 return Glib::Variant<Glib::ustring>::create(PyString_AsString(input));
58aa1f83
ML
427 else if (type == G_VARIANT_TYPE_BOOLEAN && PyBool_Check(input))
428 return Glib::Variant<bool>::create(input == Py_True);
429 else if (type == G_VARIANT_TYPE_DOUBLE && PyFloat_Check(input))
430 return Glib::Variant<double>::create(PyFloat_AsDouble(input));
431 else if (type == G_VARIANT_TYPE_INT32 && PyInt_Check(input))
432 return Glib::Variant<gint32>::create(PyInt_AsLong(input));
433 else
434 throw sigrok::Error(SR_ERR_ARG);
435}
436
437/* Convert from a Python dict to a std::map<std::string, std::string> */
438std::map<std::string, Glib::VariantBase> dict_to_map_options(PyObject *dict,
439 std::map<std::string, std::shared_ptr<sigrok::Option> > options)
440{
441 if (!PyDict_Check(dict))
442 throw sigrok::Error(SR_ERR_ARG);
443
444 std::map<std::string, Glib::VariantBase> output;
445
446 PyObject *py_key, *py_value;
447 Py_ssize_t pos = 0;
448
449 while (PyDict_Next(dict, &pos, &py_key, &py_value)) {
450 if (!PyString_Check(py_key))
451 throw sigrok::Error(SR_ERR_ARG);
452 auto key = PyString_AsString(py_key);
453 auto value = python_to_variant_by_option(py_value, options[key]);
454 output[key] = value;
455 }
456
457 return output;
458}
459
f7740954
ML
460%}
461
462/* Ignore these methods, we will override them below. */
ed6b4c47 463%ignore sigrok::Analog::data;
f7740954 464%ignore sigrok::Driver::scan;
ca3291e3 465%ignore sigrok::InputFormat::create_input;
f7740954
ML
466%ignore sigrok::OutputFormat::create_output;
467
bd4fda24
ML
468%include "doc.i"
469
062430a2 470%define %attributevector(Class, Type, Name, Get)
444d6a39
ML
471%rename(_ ## Get) sigrok::Class::Get;
472%extend sigrok::Class
473{
474%pythoncode
475{
476 Name = property(_ ## Get)
477}
478}
062430a2
ML
479%enddef
480
481%define %attributemap(Class, Type, Name, Get)
444d6a39
ML
482%rename(_ ## Get) sigrok::Class::Get;
483%extend sigrok::Class
484{
485%pythoncode
486{
487 Name = property(fget = lambda x: x._ ## Get().asdict(), doc=_ ## Get.__doc__)
488}
489}
062430a2
ML
490%enddef
491
7a36ceac 492%define %enumextras(Class)
f0c0dab5
ML
493%extend sigrok::Class
494{
495 long __hash__()
496 {
497 return (long) $self;
498 }
499
500%pythoncode
501{
502 def __eq__(self, other):
503 return (type(self) is type(other) and hash(self) == hash(other))
504
505 def __ne__(self, other):
506 return (type(self) is not type(other) or hash(self) != hash(other))
507}
508}
7a36ceac
ML
509%enddef
510
f7740954
ML
511%include "../../../swig/classes.i"
512
513/* Support Driver.scan() with keyword arguments. */
514%extend sigrok::Driver
515{
516 std::vector<std::shared_ptr<sigrok::HardwareDevice> > _scan_kwargs(PyObject *dict)
517 {
518 if (!PyDict_Check(dict))
519 throw sigrok::Error(SR_ERR_ARG);
520
521 PyObject *py_key, *py_value;
522 Py_ssize_t pos = 0;
523 std::map<const sigrok::ConfigKey *, Glib::VariantBase> options;
524
525 while (PyDict_Next(dict, &pos, &py_key, &py_value))
526 {
527 if (!PyString_Check(py_key))
528 throw sigrok::Error(SR_ERR_ARG);
0bc1a761 529 auto key = sigrok::ConfigKey::get_by_identifier(PyString_AsString(py_key));
f7740954
ML
530 auto value = python_to_variant_by_key(py_value, key);
531 options[key] = value;
532 }
533
534 return $self->scan(options);
535 }
536}
537
538%pythoncode
539{
540 def _Driver_scan(self, **kwargs):
541 return self._scan_kwargs(kwargs)
542
543 Driver.scan = _Driver_scan
544}
545
ca3291e3 546/* Support InputFormat.create_input() with keyword arguments. */
f7740954
ML
547%extend sigrok::InputFormat
548{
ca3291e3 549 std::shared_ptr<sigrok::Input> _create_input_kwargs(PyObject *dict)
f7740954 550 {
ca3291e3 551 return $self->create_input(
3b161085 552 dict_to_map_options(dict, $self->options()));
f7740954
ML
553 }
554}
555
556%pythoncode
557{
ca3291e3
ML
558 def _InputFormat_create_input(self, **kwargs):
559 return self._create_input(kwargs)
f7740954 560
ca3291e3 561 InputFormat.create_input = _InputFormat_create_input
f7740954
ML
562}
563
564/* Support OutputFormat.create_output() with keyword arguments. */
565%extend sigrok::OutputFormat
566{
567 std::shared_ptr<sigrok::Output> _create_output_kwargs(
568 std::shared_ptr<sigrok::Device> device, PyObject *dict)
569 {
58aa1f83 570 return $self->create_output(device,
3b161085 571 dict_to_map_options(dict, $self->options()));
f7740954
ML
572 }
573}
574
575%pythoncode
576{
577 def _OutputFormat_create_output(self, device, **kwargs):
578 return self._create_output_kwargs(device, kwargs)
579
580 OutputFormat.create_output = _OutputFormat_create_output
581}
582
583/* Support config_set() with Python input types. */
584%extend sigrok::Configurable
585{
586 void config_set(const ConfigKey *key, PyObject *input)
587 {
588 $self->config_set(key, python_to_variant_by_key(input, key));
589 }
590}
ed6b4c47
ML
591
592/* Return NumPy array from Analog::data(). */
593%extend sigrok::Analog
594{
595 PyObject * _data()
596 {
597 int nd = 2;
598 npy_intp dims[2];
599 dims[0] = $self->channels().size();
600 dims[1] = $self->num_samples();
601 int typenum = NPY_FLOAT;
602 void *data = $self->data_pointer();
603 return PyArray_SimpleNewFromData(nd, dims, typenum, data);
604 }
605
606%pythoncode
607{
608 data = property(_data)
609}
610}