]> sigrok.org Git - libsigrok.git/blame_incremental - bindings/python/sigrok/core/classes.i
python bindings: Support passing in rational voltages for config_set
[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 "config.h"
49
50#include <stdio.h>
51#include <pygobject.h>
52#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
53#include <numpy/arrayobject.h>
54
55PyObject *PyGObject_lib;
56PyObject *GLib;
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/* Use the same typemap above for Glib::VariantContainerBase */
116%apply Glib::VariantBase { Glib::VariantContainerBase }
117
118/* Map from callable PyObject to LogCallbackFunction */
119%typecheck(SWIG_TYPECHECK_POINTER) sigrok::LogCallbackFunction {
120 $1 = PyCallable_Check($input);
121}
122
123%typemap(in) sigrok::LogCallbackFunction {
124 if (!PyCallable_Check($input))
125 SWIG_exception(SWIG_TypeError, "Expected a callable Python object");
126
127 $1 = [=] (const sigrok::LogLevel *loglevel, std::string message) {
128 auto gstate = PyGILState_Ensure();
129
130 auto log_obj = SWIG_NewPointerObj(
131 SWIG_as_voidptr(loglevel), SWIGTYPE_p_sigrok__LogLevel, 0);
132
133 auto string_obj = string_to_python(message.c_str());
134
135 auto arglist = Py_BuildValue("(OO)", log_obj, string_obj);
136
137 auto result = PyEval_CallObject($input, arglist);
138
139 Py_XDECREF(arglist);
140 Py_XDECREF(log_obj);
141 Py_XDECREF(string_obj);
142
143 bool completed = !PyErr_Occurred();
144
145 if (!completed)
146 PyErr_Print();
147
148 bool valid_result = (completed && result == Py_None);
149
150 Py_XDECREF(result);
151
152 if (completed && !valid_result)
153 {
154 PyErr_SetString(PyExc_TypeError,
155 "Log callback did not return None");
156 PyErr_Print();
157 }
158
159 PyGILState_Release(gstate);
160
161 if (!valid_result)
162 throw sigrok::Error(SR_ERR);
163 };
164
165 Py_XINCREF($input);
166}
167
168/* Map from callable PyObject to SessionStoppedCallback */
169%typecheck(SWIG_TYPECHECK_POINTER) sigrok::SessionStoppedCallback {
170 $1 = PyCallable_Check($input);
171}
172
173%typemap(in) sigrok::SessionStoppedCallback {
174 if (!PyCallable_Check($input))
175 SWIG_exception(SWIG_TypeError, "Expected a callable Python object");
176
177 $1 = [=] () {
178 const auto gstate = PyGILState_Ensure();
179
180 const auto result = PyEval_CallObject($input, nullptr);
181 const bool completed = !PyErr_Occurred();
182 const bool valid_result = (completed && result == Py_None);
183
184 if (completed && !valid_result) {
185 PyErr_SetString(PyExc_TypeError,
186 "Session stop callback did not return None");
187 }
188 if (!valid_result)
189 PyErr_Print();
190
191 Py_XDECREF(result);
192 PyGILState_Release(gstate);
193
194 if (!valid_result)
195 throw sigrok::Error(SR_ERR);
196 };
197
198 Py_XINCREF($input);
199}
200
201/* Map from callable PyObject to DatafeedCallbackFunction */
202%typecheck(SWIG_TYPECHECK_POINTER) sigrok::DatafeedCallbackFunction {
203 $1 = PyCallable_Check($input);
204}
205
206%typemap(in) sigrok::DatafeedCallbackFunction {
207 if (!PyCallable_Check($input))
208 SWIG_exception(SWIG_TypeError, "Expected a callable Python object");
209
210 $1 = [=] (std::shared_ptr<sigrok::Device> device,
211 std::shared_ptr<sigrok::Packet> packet) {
212 auto gstate = PyGILState_Ensure();
213
214 auto device_obj = SWIG_NewPointerObj(
215 SWIG_as_voidptr(new std::shared_ptr<sigrok::Device>(device)),
216 SWIGTYPE_p_std__shared_ptrT_sigrok__Device_t, SWIG_POINTER_OWN);
217
218 auto packet_obj = SWIG_NewPointerObj(
219 SWIG_as_voidptr(new std::shared_ptr<sigrok::Packet>(packet)),
220 SWIGTYPE_p_std__shared_ptrT_sigrok__Packet_t, SWIG_POINTER_OWN);
221
222 auto arglist = Py_BuildValue("(OO)", device_obj, packet_obj);
223
224 auto result = PyEval_CallObject($input, arglist);
225
226 Py_XDECREF(arglist);
227 Py_XDECREF(device_obj);
228 Py_XDECREF(packet_obj);
229
230 bool completed = !PyErr_Occurred();
231
232 if (!completed)
233 PyErr_Print();
234
235 bool valid_result = (completed && result == Py_None);
236
237 Py_XDECREF(result);
238
239 if (completed && !valid_result)
240 {
241 PyErr_SetString(PyExc_TypeError,
242 "Datafeed callback did not return None");
243 PyErr_Print();
244 }
245
246 PyGILState_Release(gstate);
247
248 if (!valid_result)
249 throw sigrok::Error(SR_ERR);
250 };
251
252 Py_XINCREF($input);
253}
254
255/* Cast PacketPayload pointers to correct subclass type. */
256%ignore sigrok::Packet::payload;
257
258%extend sigrok::Packet
259{
260 std::shared_ptr<sigrok::Header> _payload_header()
261 {
262 return dynamic_pointer_cast<sigrok::Header>($self->payload());
263 }
264 std::shared_ptr<sigrok::Meta> _payload_meta()
265 {
266 return dynamic_pointer_cast<sigrok::Meta>($self->payload());
267 }
268 std::shared_ptr<sigrok::Analog> _payload_analog()
269 {
270 return dynamic_pointer_cast<sigrok::Analog>($self->payload());
271 }
272 std::shared_ptr<sigrok::Logic> _payload_logic()
273 {
274 return dynamic_pointer_cast<sigrok::Logic>($self->payload());
275 }
276}
277
278%extend sigrok::Packet
279{
280%pythoncode
281{
282 def _payload(self):
283 if self.type == PacketType.HEADER:
284 return self._payload_header()
285 elif self.type == PacketType.META:
286 return self._payload_meta()
287 elif self.type == PacketType.LOGIC:
288 return self._payload_logic()
289 elif self.type == PacketType.ANALOG:
290 return self._payload_analog()
291 else:
292 return None
293
294 payload = property(_payload)
295}
296}
297
298%{
299
300#include "libsigrokcxx/libsigrokcxx.hpp"
301
302/* Convert from a Python dict to a std::map<std::string, std::string> */
303std::map<std::string, std::string> dict_to_map_string(PyObject *dict)
304{
305 if (!PyDict_Check(dict))
306 throw sigrok::Error(SR_ERR_ARG);
307
308 std::map<std::string, std::string> output;
309
310 PyObject *py_key, *py_value;
311 Py_ssize_t pos = 0;
312
313 while (PyDict_Next(dict, &pos, &py_key, &py_value)) {
314 if (!string_check(py_key))
315 throw sigrok::Error(SR_ERR_ARG);
316 if (!string_check(py_value))
317 throw sigrok::Error(SR_ERR_ARG);
318 auto key = string_from_python(py_key);
319 auto value = string_from_python(py_value);
320 output[key] = value;
321 }
322
323 return output;
324}
325
326/* Convert from a Python type to Glib::Variant, according to config key data type. */
327Glib::VariantBase python_to_variant_by_key(PyObject *input, const sigrok::ConfigKey *key)
328{
329 enum sr_datatype type = (enum sr_datatype) key->data_type()->id();
330
331 if (type == SR_T_UINT64 && PyInt_Check(input))
332 return Glib::Variant<guint64>::create(PyInt_AsLong(input));
333 if (type == SR_T_UINT64 && PyLong_Check(input))
334 return Glib::Variant<guint64>::create(PyLong_AsLong(input));
335 else if (type == SR_T_STRING && string_check(input))
336 return Glib::Variant<Glib::ustring>::create(string_from_python(input));
337 else if (type == SR_T_BOOL && PyBool_Check(input))
338 return Glib::Variant<bool>::create(input == Py_True);
339 else if (type == SR_T_FLOAT && PyFloat_Check(input))
340 return Glib::Variant<double>::create(PyFloat_AsDouble(input));
341 else if (type == SR_T_INT32 && PyInt_Check(input))
342 return Glib::Variant<gint32>::create(PyInt_AsLong(input));
343 else if ((type == SR_T_RATIONAL_VOLT) && PyTuple_Check(input) && (PyTuple_Size(input) == 2)) {
344 PyObject *numObj = PyTuple_GetItem(input, 0);
345 PyObject *denomObj = PyTuple_GetItem(input, 1);
346 if ((PyInt_Check(numObj) || PyLong_Check(numObj)) && (PyInt_Check(denomObj) || PyLong_Check(denomObj))) {
347 std::tuple<guint64, guint64> tpl = {PyInt_AsLong(numObj), PyInt_AsLong(denomObj)};
348 return Glib::Variant< std::tuple<guint64,guint64> >::Variant::create(tpl);
349 }
350 }
351 throw sigrok::Error(SR_ERR_ARG);
352}
353
354/* Convert from a Python type to Glib::Variant, according to Option data type. */
355Glib::VariantBase python_to_variant_by_option(PyObject *input,
356 std::shared_ptr<sigrok::Option> option)
357{
358 GVariantType *type = option->default_value().get_type().gobj();
359
360 if (type == G_VARIANT_TYPE_UINT64 && PyInt_Check(input))
361 return Glib::Variant<guint64>::create(PyInt_AsLong(input));
362 if (type == G_VARIANT_TYPE_UINT64 && PyLong_Check(input))
363 return Glib::Variant<guint64>::create(PyLong_AsLong(input));
364 else if (type == G_VARIANT_TYPE_STRING && string_check(input))
365 return Glib::Variant<Glib::ustring>::create(string_from_python(input));
366 else if (type == G_VARIANT_TYPE_BOOLEAN && PyBool_Check(input))
367 return Glib::Variant<bool>::create(input == Py_True);
368 else if (type == G_VARIANT_TYPE_DOUBLE && PyFloat_Check(input))
369 return Glib::Variant<double>::create(PyFloat_AsDouble(input));
370 else if (type == G_VARIANT_TYPE_INT32 && PyInt_Check(input))
371 return Glib::Variant<gint32>::create(PyInt_AsLong(input));
372 else
373 throw sigrok::Error(SR_ERR_ARG);
374}
375
376/* Convert from a Python dict to a std::map<std::string, std::string> */
377std::map<std::string, Glib::VariantBase> dict_to_map_options(PyObject *dict,
378 std::map<std::string, std::shared_ptr<sigrok::Option> > options)
379{
380 if (!PyDict_Check(dict))
381 throw sigrok::Error(SR_ERR_ARG);
382
383 std::map<std::string, Glib::VariantBase> output;
384
385 PyObject *py_key, *py_value;
386 Py_ssize_t pos = 0;
387
388 while (PyDict_Next(dict, &pos, &py_key, &py_value)) {
389 if (!string_check(py_key))
390 throw sigrok::Error(SR_ERR_ARG);
391 auto key = string_from_python(py_key);
392 auto value = python_to_variant_by_option(py_value, options[key]);
393 output[key] = value;
394 }
395
396 return output;
397}
398
399%}
400
401/* Ignore these methods, we will override them below. */
402%ignore sigrok::Analog::data;
403%ignore sigrok::Logic::data;
404%ignore sigrok::Driver::scan;
405%ignore sigrok::InputFormat::create_input;
406%ignore sigrok::OutputFormat::create_output;
407
408%include "doc_start.i"
409
410%define %attributevector(Class, Type, Name, Get)
411%rename(_ ## Get) sigrok::Class::Get;
412%extend sigrok::Class
413{
414%pythoncode
415{
416 Name = property(_ ## Get)
417}
418}
419%enddef
420
421%define %attributemap(Class, Type, Name, Get)
422%rename(_ ## Get) sigrok::Class::Get;
423%extend sigrok::Class
424{
425%pythoncode
426{
427 Name = property(fget = lambda x: x._ ## Get().asdict(), doc=_ ## Get.__doc__)
428}
429}
430%enddef
431
432%define %enumextras(Class)
433%extend sigrok::Class
434{
435 long __hash__()
436 {
437 return (long) $self;
438 }
439
440 std::string __str__()
441 {
442 return $self->name();
443 }
444
445 std::string __repr__()
446 {
447 return "Class." + $self->name();
448 }
449
450%pythoncode
451{
452 def __eq__(self, other):
453 return (type(self) is type(other) and hash(self) == hash(other))
454
455 def __ne__(self, other):
456 return (type(self) is not type(other) or hash(self) != hash(other))
457}
458}
459%enddef
460
461%include "../../../swig/classes.i"
462
463/* Support Driver.scan() with keyword arguments. */
464%extend sigrok::Driver
465{
466 std::vector<std::shared_ptr<sigrok::HardwareDevice> > _scan_kwargs(PyObject *dict)
467 {
468 if (!PyDict_Check(dict))
469 throw sigrok::Error(SR_ERR_ARG);
470
471 PyObject *py_key, *py_value;
472 Py_ssize_t pos = 0;
473 std::map<const sigrok::ConfigKey *, Glib::VariantBase> options;
474
475 while (PyDict_Next(dict, &pos, &py_key, &py_value))
476 {
477 if (!string_check(py_key))
478 throw sigrok::Error(SR_ERR_ARG);
479 auto key = sigrok::ConfigKey::get_by_identifier(string_from_python(py_key));
480 auto value = python_to_variant_by_key(py_value, key);
481 options[key] = value;
482 }
483
484 return $self->scan(options);
485 }
486}
487
488%pythoncode
489{
490 def _Driver_scan(self, **kwargs):
491 return self._scan_kwargs(kwargs)
492
493 Driver.scan = _Driver_scan
494}
495
496/* Support InputFormat.create_input() with keyword arguments. */
497%extend sigrok::InputFormat
498{
499 std::shared_ptr<sigrok::Input> _create_input_kwargs(PyObject *dict)
500 {
501 return $self->create_input(
502 dict_to_map_options(dict, $self->options()));
503 }
504}
505
506%pythoncode
507{
508 def _InputFormat_create_input(self, **kwargs):
509 return self._create_input(kwargs)
510
511 InputFormat.create_input = _InputFormat_create_input
512}
513
514/* Support OutputFormat.create_output() with keyword arguments. */
515%extend sigrok::OutputFormat
516{
517 std::shared_ptr<sigrok::Output> _create_output_kwargs(
518 std::shared_ptr<sigrok::Device> device, PyObject *dict)
519 {
520 return $self->create_output(device,
521 dict_to_map_options(dict, $self->options()));
522 }
523}
524
525%pythoncode
526{
527 def _OutputFormat_create_output(self, device, **kwargs):
528 return self._create_output_kwargs(device, kwargs)
529
530 OutputFormat.create_output = _OutputFormat_create_output
531}
532
533/* Support config_set() with Python input types. */
534%extend sigrok::Configurable
535{
536 void config_set(const ConfigKey *key, PyObject *input)
537 {
538 $self->config_set(key, python_to_variant_by_key(input, key));
539 }
540}
541
542/* Return NumPy array from Analog::data(). */
543%extend sigrok::Analog
544{
545 PyObject * _data()
546 {
547 int nd = 2;
548 npy_intp dims[2];
549 dims[0] = $self->channels().size();
550 dims[1] = $self->num_samples();
551 int typenum = NPY_FLOAT;
552 void *data = $self->data_pointer();
553 return PyArray_SimpleNewFromData(nd, dims, typenum, data);
554 }
555
556%pythoncode
557{
558 data = property(_data)
559}
560}
561
562/* Return NumPy array from Logic::data(). */
563%extend sigrok::Logic
564{
565 PyObject * _data()
566 {
567 npy_intp dims[2];
568 dims[0] = $self->data_length() / $self->unit_size();
569 dims[1] = $self->unit_size();
570 int typenum = NPY_UINT8;
571 void *data = $self->data_pointer();
572 return PyArray_SimpleNewFromData(2, dims, typenum, data);
573 }
574
575%pythoncode
576{
577 data = property(_data)
578}
579}
580
581/* Create logic packet from Python buffer. */
582%extend sigrok::Context
583{
584 std::shared_ptr<Packet> _create_logic_packet_buf(PyObject *buf, unsigned int unit_size)
585 {
586 Py_buffer view;
587 PyObject_GetBuffer(buf, &view, PyBUF_SIMPLE);
588 return $self->create_logic_packet(view.buf, view.len, unit_size);
589 }
590}
591
592%pythoncode
593{
594 def _Context_create_logic_packet(self, buf, unit_size):
595 return self._create_logic_packet_buf(buf, unit_size)
596
597 Context.create_logic_packet = _Context_create_logic_packet
598}
599
600%include "doc_end.i"