]> sigrok.org Git - libsigrok.git/blame_incremental - bindings/python/sigrok/core/classes.i
python: Provide sensible __str__ and __repr__ functions for enum values.
[libsigrok.git] / bindings / python / sigrok / core / classes.i
... / ...
CommitLineData
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
20%define DOCSTRING
21"@mainpage API Reference
22
23Introduction
24------------
25
26The pysigrok API provides an object-oriented Python interface to the
27functionality in libsigrok. It is built on top of the libsigrokcxx C++ API.
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
46
47%{
48#include <stdio.h>
49#include <pygobject.h>
50#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
51#include <numpy/arrayobject.h>
52
53PyObject *PyGObject_lib;
54PyObject *GLib;
55
56#include "config.h"
57
58#if PYGOBJECT_FLAGS_SIGNED
59typedef gint pyg_flags_type;
60#else
61typedef guint pyg_flags_type;
62#endif
63
64#if PY_VERSION_HEX >= 0x03000000
65#define string_check PyUnicode_Check
66#define string_from_python PyUnicode_AsUTF8
67#define string_to_python PyUnicode_FromString
68#else
69#define string_check PyString_Check
70#define string_from_python PyString_AsString
71#define string_to_python PyString_FromString
72#endif
73
74%}
75
76%init %{
77 PyGObject_lib = pygobject_init(-1, -1, -1);
78 if (!PyGObject_lib)
79 fprintf(stderr, "pygobject initialization failed.\n");
80 GLib = PyImport_ImportModule("gi.repository.GLib");
81 /*
82 * This check can't save us if the import fails, but at least it gives us
83 * a starting point to trace the issue versus straight out crashing.
84 */
85 if (!GLib) {
86 fprintf(stderr, "Import of gi.repository.GLib failed.\n");
87#if PY_VERSION_HEX >= 0x03000000
88 return nullptr;
89#else
90 return;
91#endif
92 }
93 import_array();
94%}
95
96%include "../../../swig/templates.i"
97
98/* Map file objects to file descriptors. */
99%typecheck(SWIG_TYPECHECK_POINTER) int fd {
100 $1 = (PyObject_AsFileDescriptor($input) != -1);
101}
102
103/* Map from Glib::Variant to native Python types. */
104%typemap(out) Glib::VariantBase {
105 GValue *value = g_new0(GValue, 1);
106 g_value_init(value, G_TYPE_VARIANT);
107 g_value_set_variant(value, $1.gobj());
108 PyObject *variant = pyg_value_as_pyobject(value, true);
109 $result = PyObject_CallMethod(variant,
110 const_cast<char *>("unpack"), nullptr);
111 Py_XDECREF(variant);
112 g_free(value);
113}
114
115/* Map from callable PyObject to LogCallbackFunction */
116%typecheck(SWIG_TYPECHECK_POINTER) sigrok::LogCallbackFunction {
117 $1 = PyCallable_Check($input);
118}
119
120%typemap(in) sigrok::LogCallbackFunction {
121 if (!PyCallable_Check($input))
122 SWIG_exception(SWIG_TypeError, "Expected a callable Python object");
123
124 $1 = [=] (const sigrok::LogLevel *loglevel, std::string message) {
125 auto gstate = PyGILState_Ensure();
126
127 auto log_obj = SWIG_NewPointerObj(
128 SWIG_as_voidptr(loglevel), SWIGTYPE_p_sigrok__LogLevel, 0);
129
130 auto string_obj = string_to_python(message.c_str());
131
132 auto arglist = Py_BuildValue("(OO)", log_obj, string_obj);
133
134 auto result = PyEval_CallObject($input, arglist);
135
136 Py_XDECREF(arglist);
137 Py_XDECREF(log_obj);
138 Py_XDECREF(string_obj);
139
140 bool completed = !PyErr_Occurred();
141
142 if (!completed)
143 PyErr_Print();
144
145 bool valid_result = (completed && result == Py_None);
146
147 Py_XDECREF(result);
148
149 if (completed && !valid_result)
150 {
151 PyErr_SetString(PyExc_TypeError,
152 "Log callback did not return None");
153 PyErr_Print();
154 }
155
156 PyGILState_Release(gstate);
157
158 if (!valid_result)
159 throw sigrok::Error(SR_ERR);
160 };
161
162 Py_XINCREF($input);
163}
164
165/* Map from callable PyObject to SessionStoppedCallback */
166%typecheck(SWIG_TYPECHECK_POINTER) sigrok::SessionStoppedCallback {
167 $1 = PyCallable_Check($input);
168}
169
170%typemap(in) sigrok::SessionStoppedCallback {
171 if (!PyCallable_Check($input))
172 SWIG_exception(SWIG_TypeError, "Expected a callable Python object");
173
174 $1 = [=] () {
175 const auto gstate = PyGILState_Ensure();
176
177 const auto result = PyEval_CallObject($input, nullptr);
178 const bool completed = !PyErr_Occurred();
179 const bool valid_result = (completed && result == Py_None);
180
181 if (completed && !valid_result) {
182 PyErr_SetString(PyExc_TypeError,
183 "Session stop callback did not return None");
184 }
185 if (!valid_result)
186 PyErr_Print();
187
188 Py_XDECREF(result);
189 PyGILState_Release(gstate);
190
191 if (!valid_result)
192 throw sigrok::Error(SR_ERR);
193 };
194
195 Py_XINCREF($input);
196}
197
198/* Map from callable PyObject to DatafeedCallbackFunction */
199%typecheck(SWIG_TYPECHECK_POINTER) sigrok::DatafeedCallbackFunction {
200 $1 = PyCallable_Check($input);
201}
202
203%typemap(in) sigrok::DatafeedCallbackFunction {
204 if (!PyCallable_Check($input))
205 SWIG_exception(SWIG_TypeError, "Expected a callable Python object");
206
207 $1 = [=] (std::shared_ptr<sigrok::Device> device,
208 std::shared_ptr<sigrok::Packet> packet) {
209 auto gstate = PyGILState_Ensure();
210
211 auto device_obj = SWIG_NewPointerObj(
212 SWIG_as_voidptr(new std::shared_ptr<sigrok::Device>(device)),
213 SWIGTYPE_p_std__shared_ptrT_sigrok__Device_t, SWIG_POINTER_OWN);
214
215 auto packet_obj = SWIG_NewPointerObj(
216 SWIG_as_voidptr(new std::shared_ptr<sigrok::Packet>(packet)),
217 SWIGTYPE_p_std__shared_ptrT_sigrok__Packet_t, SWIG_POINTER_OWN);
218
219 auto arglist = Py_BuildValue("(OO)", device_obj, packet_obj);
220
221 auto result = PyEval_CallObject($input, arglist);
222
223 Py_XDECREF(arglist);
224 Py_XDECREF(device_obj);
225 Py_XDECREF(packet_obj);
226
227 bool completed = !PyErr_Occurred();
228
229 if (!completed)
230 PyErr_Print();
231
232 bool valid_result = (completed && result == Py_None);
233
234 Py_XDECREF(result);
235
236 if (completed && !valid_result)
237 {
238 PyErr_SetString(PyExc_TypeError,
239 "Datafeed callback did not return None");
240 PyErr_Print();
241 }
242
243 PyGILState_Release(gstate);
244
245 if (!valid_result)
246 throw sigrok::Error(SR_ERR);
247 };
248
249 Py_XINCREF($input);
250}
251
252/* Cast PacketPayload pointers to correct subclass type. */
253%ignore sigrok::Packet::payload;
254
255%extend sigrok::Packet
256{
257 std::shared_ptr<sigrok::Header> _payload_header()
258 {
259 return dynamic_pointer_cast<sigrok::Header>($self->payload());
260 }
261 std::shared_ptr<sigrok::Meta> _payload_meta()
262 {
263 return dynamic_pointer_cast<sigrok::Meta>($self->payload());
264 }
265 std::shared_ptr<sigrok::Analog> _payload_analog()
266 {
267 return dynamic_pointer_cast<sigrok::Analog>($self->payload());
268 }
269 std::shared_ptr<sigrok::Logic> _payload_logic()
270 {
271 return dynamic_pointer_cast<sigrok::Logic>($self->payload());
272 }
273}
274
275%extend sigrok::Packet
276{
277%pythoncode
278{
279 def _payload(self):
280 if self.type == PacketType.HEADER:
281 return self._payload_header()
282 elif self.type == PacketType.META:
283 return self._payload_meta()
284 elif self.type == PacketType.LOGIC:
285 return self._payload_logic()
286 elif self.type == PacketType.ANALOG:
287 return self._payload_analog()
288 else:
289 return None
290
291 payload = property(_payload)
292}
293}
294
295%{
296
297#include "libsigrokcxx/libsigrokcxx.hpp"
298
299/* Convert from a Python dict to a std::map<std::string, std::string> */
300std::map<std::string, std::string> dict_to_map_string(PyObject *dict)
301{
302 if (!PyDict_Check(dict))
303 throw sigrok::Error(SR_ERR_ARG);
304
305 std::map<std::string, std::string> output;
306
307 PyObject *py_key, *py_value;
308 Py_ssize_t pos = 0;
309
310 while (PyDict_Next(dict, &pos, &py_key, &py_value)) {
311 if (!string_check(py_key))
312 throw sigrok::Error(SR_ERR_ARG);
313 if (!string_check(py_value))
314 throw sigrok::Error(SR_ERR_ARG);
315 auto key = string_from_python(py_key);
316 auto value = string_from_python(py_value);
317 output[key] = value;
318 }
319
320 return output;
321}
322
323/* Convert from a Python type to Glib::Variant, according to config key data type. */
324Glib::VariantBase python_to_variant_by_key(PyObject *input, const sigrok::ConfigKey *key)
325{
326 enum sr_datatype type = (enum sr_datatype) key->data_type()->id();
327
328 if (type == SR_T_UINT64 && PyInt_Check(input))
329 return Glib::Variant<guint64>::create(PyInt_AsLong(input));
330 if (type == SR_T_UINT64 && PyLong_Check(input))
331 return Glib::Variant<guint64>::create(PyLong_AsLong(input));
332 else if (type == SR_T_STRING && string_check(input))
333 return Glib::Variant<Glib::ustring>::create(string_from_python(input));
334 else if (type == SR_T_BOOL && PyBool_Check(input))
335 return Glib::Variant<bool>::create(input == Py_True);
336 else if (type == SR_T_FLOAT && PyFloat_Check(input))
337 return Glib::Variant<double>::create(PyFloat_AsDouble(input));
338 else if (type == SR_T_INT32 && PyInt_Check(input))
339 return Glib::Variant<gint32>::create(PyInt_AsLong(input));
340 else
341 throw sigrok::Error(SR_ERR_ARG);
342}
343
344/* Convert from a Python type to Glib::Variant, according to Option data type. */
345Glib::VariantBase python_to_variant_by_option(PyObject *input,
346 std::shared_ptr<sigrok::Option> option)
347{
348 GVariantType *type = option->default_value().get_type().gobj();
349
350 if (type == G_VARIANT_TYPE_UINT64 && PyInt_Check(input))
351 return Glib::Variant<guint64>::create(PyInt_AsLong(input));
352 if (type == G_VARIANT_TYPE_UINT64 && PyLong_Check(input))
353 return Glib::Variant<guint64>::create(PyLong_AsLong(input));
354 else if (type == G_VARIANT_TYPE_STRING && string_check(input))
355 return Glib::Variant<Glib::ustring>::create(string_from_python(input));
356 else if (type == G_VARIANT_TYPE_BOOLEAN && PyBool_Check(input))
357 return Glib::Variant<bool>::create(input == Py_True);
358 else if (type == G_VARIANT_TYPE_DOUBLE && PyFloat_Check(input))
359 return Glib::Variant<double>::create(PyFloat_AsDouble(input));
360 else if (type == G_VARIANT_TYPE_INT32 && PyInt_Check(input))
361 return Glib::Variant<gint32>::create(PyInt_AsLong(input));
362 else
363 throw sigrok::Error(SR_ERR_ARG);
364}
365
366/* Convert from a Python dict to a std::map<std::string, std::string> */
367std::map<std::string, Glib::VariantBase> dict_to_map_options(PyObject *dict,
368 std::map<std::string, std::shared_ptr<sigrok::Option> > options)
369{
370 if (!PyDict_Check(dict))
371 throw sigrok::Error(SR_ERR_ARG);
372
373 std::map<std::string, Glib::VariantBase> output;
374
375 PyObject *py_key, *py_value;
376 Py_ssize_t pos = 0;
377
378 while (PyDict_Next(dict, &pos, &py_key, &py_value)) {
379 if (!string_check(py_key))
380 throw sigrok::Error(SR_ERR_ARG);
381 auto key = string_from_python(py_key);
382 auto value = python_to_variant_by_option(py_value, options[key]);
383 output[key] = value;
384 }
385
386 return output;
387}
388
389%}
390
391/* Ignore these methods, we will override them below. */
392%ignore sigrok::Analog::data;
393%ignore sigrok::Driver::scan;
394%ignore sigrok::InputFormat::create_input;
395%ignore sigrok::OutputFormat::create_output;
396
397%include "doc_start.i"
398
399%define %attributevector(Class, Type, Name, Get)
400%rename(_ ## Get) sigrok::Class::Get;
401%extend sigrok::Class
402{
403%pythoncode
404{
405 Name = property(_ ## Get)
406}
407}
408%enddef
409
410%define %attributemap(Class, Type, Name, Get)
411%rename(_ ## Get) sigrok::Class::Get;
412%extend sigrok::Class
413{
414%pythoncode
415{
416 Name = property(fget = lambda x: x._ ## Get().asdict(), doc=_ ## Get.__doc__)
417}
418}
419%enddef
420
421%define %enumextras(Class)
422%extend sigrok::Class
423{
424 long __hash__()
425 {
426 return (long) $self;
427 }
428
429 std::string __str__()
430 {
431 return $self->name();
432 }
433
434 std::string __repr__()
435 {
436 return "Class." + $self->name();
437 }
438
439%pythoncode
440{
441 def __eq__(self, other):
442 return (type(self) is type(other) and hash(self) == hash(other))
443
444 def __ne__(self, other):
445 return (type(self) is not type(other) or hash(self) != hash(other))
446}
447}
448%enddef
449
450%include "../../../swig/classes.i"
451
452/* Support Driver.scan() with keyword arguments. */
453%extend sigrok::Driver
454{
455 std::vector<std::shared_ptr<sigrok::HardwareDevice> > _scan_kwargs(PyObject *dict)
456 {
457 if (!PyDict_Check(dict))
458 throw sigrok::Error(SR_ERR_ARG);
459
460 PyObject *py_key, *py_value;
461 Py_ssize_t pos = 0;
462 std::map<const sigrok::ConfigKey *, Glib::VariantBase> options;
463
464 while (PyDict_Next(dict, &pos, &py_key, &py_value))
465 {
466 if (!string_check(py_key))
467 throw sigrok::Error(SR_ERR_ARG);
468 auto key = sigrok::ConfigKey::get_by_identifier(string_from_python(py_key));
469 auto value = python_to_variant_by_key(py_value, key);
470 options[key] = value;
471 }
472
473 return $self->scan(options);
474 }
475}
476
477%pythoncode
478{
479 def _Driver_scan(self, **kwargs):
480 return self._scan_kwargs(kwargs)
481
482 Driver.scan = _Driver_scan
483}
484
485/* Support InputFormat.create_input() with keyword arguments. */
486%extend sigrok::InputFormat
487{
488 std::shared_ptr<sigrok::Input> _create_input_kwargs(PyObject *dict)
489 {
490 return $self->create_input(
491 dict_to_map_options(dict, $self->options()));
492 }
493}
494
495%pythoncode
496{
497 def _InputFormat_create_input(self, **kwargs):
498 return self._create_input(kwargs)
499
500 InputFormat.create_input = _InputFormat_create_input
501}
502
503/* Support OutputFormat.create_output() with keyword arguments. */
504%extend sigrok::OutputFormat
505{
506 std::shared_ptr<sigrok::Output> _create_output_kwargs(
507 std::shared_ptr<sigrok::Device> device, PyObject *dict)
508 {
509 return $self->create_output(device,
510 dict_to_map_options(dict, $self->options()));
511 }
512}
513
514%pythoncode
515{
516 def _OutputFormat_create_output(self, device, **kwargs):
517 return self._create_output_kwargs(device, kwargs)
518
519 OutputFormat.create_output = _OutputFormat_create_output
520}
521
522/* Support config_set() with Python input types. */
523%extend sigrok::Configurable
524{
525 void config_set(const ConfigKey *key, PyObject *input)
526 {
527 $self->config_set(key, python_to_variant_by_key(input, key));
528 }
529}
530
531/* Return NumPy array from Analog::data(). */
532%extend sigrok::Analog
533{
534 PyObject * _data()
535 {
536 int nd = 2;
537 npy_intp dims[2];
538 dims[0] = $self->channels().size();
539 dims[1] = $self->num_samples();
540 int typenum = NPY_FLOAT;
541 void *data = $self->data_pointer();
542 return PyArray_SimpleNewFromData(nd, dims, typenum, data);
543 }
544
545%pythoncode
546{
547 data = property(_data)
548}
549}
550
551%include "doc_end.i"