python: Get rid of the last C++-style comments.
[gpgme.git] / lang / python / gpgme.i
1 /*
2 # Copyright (C) 2016 g10 Code GmbH
3 # Copyright (C) 2004,2008 Igor Belyi <belyi@users.sourceforge.net>
4 # Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
5 #
6 #    This library is free software; you can redistribute it and/or
7 #    modify it under the terms of the GNU Lesser General Public
8 #    License as published by the Free Software Foundation; either
9 #    version 2.1 of the License, or (at your option) any later version.
10 #
11 #    This library 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 GNU
14 #    Lesser General Public License for more details.
15 #
16 #    You should have received a copy of the GNU Lesser General Public
17 #    License along with this library; if not, write to the Free Software
18 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
19 */
20 %module gpgme
21 %include "cpointer.i"
22 %include "cstring.i"
23
24 /* Generate doc strings for all methods.
25
26    This will generate docstrings of the form
27
28      gpgme_op_encrypt(ctx, recp, flags, plain, cipher) -> gpgme_error_t
29
30    which we transform into
31
32      ctx.op_encrypt(recp, flags, plain, cipher) -> gpgme_error_t
33
34    for automagically wrapped functions.  */
35 %feature("autodoc", "0");
36
37
38 /* Allow use of Unicode objects, bytes, and None for strings.  */
39 %typemap(in) const char *(PyObject *encodedInput = NULL) {
40   if ($input == Py_None)
41     $1 = NULL;
42   else if (PyUnicode_Check($input))
43     {
44       encodedInput = PyUnicode_AsUTF8String($input);
45       if (encodedInput == NULL)
46         return NULL;
47       $1 = PyBytes_AsString(encodedInput);
48     }
49   else if (PyBytes_Check($input))
50     $1 = PyBytes_AsString($input);
51   else {
52     PyErr_Format(PyExc_TypeError,
53                  "arg %d: expected str, bytes, or None, got %s",
54                  $argnum, $input->ob_type->tp_name);
55     return NULL;
56   }
57 }
58 %typemap(freearg) const char * {
59   Py_XDECREF(encodedInput$argnum);
60 }
61
62 /* Likewise for a list of strings.  */
63 %typemap(in) const char *[] (void *vector = NULL,
64                              size_t size,
65                              PyObject **pyVector = NULL) {
66   /* Check if is a list */
67   if (PyList_Check($input)) {
68     size_t i, j;
69     size = PyList_Size($input);
70     $1 = (char **) (vector = malloc((size+1) * sizeof(char *)));
71     pyVector = calloc(sizeof *pyVector, size);
72
73     for (i = 0; i < size; i++) {
74       PyObject *o = PyList_GetItem($input,i);
75       if (PyUnicode_Check(o))
76         {
77           pyVector[i] = PyUnicode_AsUTF8String(o);
78           if (pyVector[i] == NULL)
79             {
80               free(vector);
81               for (j = 0; j < i; j++)
82                 Py_XDECREF(pyVector[j]);
83               return NULL;
84             }
85           $1[i] = PyBytes_AsString(pyVector[i]);
86         }
87       else if (PyString_Check(o))
88         $1[i] = PyString_AsString(o);
89       else {
90         PyErr_Format(PyExc_TypeError,
91                      "arg %d: list must contain only str or bytes, got %s "
92                      "at position %d",
93                      $argnum, o->ob_type->tp_name, i);
94         free($1);
95         return NULL;
96       }
97     }
98     $1[i] = NULL;
99   } else {
100     PyErr_Format(PyExc_TypeError,
101                  "arg %d: expected a list of str or bytes, got %s",
102                  $argnum, $input->ob_type->tp_name);
103     return NULL;
104   }
105 }
106 %typemap(freearg) const char *[] {
107   size_t i;
108   free(vector$argnum);
109   for (i = 0; i < size$argnum; i++)
110     Py_XDECREF(pyVector$argnum[i]);
111 }
112
113 /* Release returned buffers as necessary.  */
114 %typemap(newfree) char * "free($1);";
115 %newobject gpgme_data_release_and_get_mem;
116
117 %typemap(arginit) gpgme_key_t [] {
118   $1 = NULL;
119 }
120
121 %typemap(in) gpgme_key_t [] {
122   int i, numb = 0;
123   if (!PySequence_Check($input)) {
124     PyErr_Format(PyExc_ValueError, "arg %d: Expected a list of gpgme_key_t",
125                  $argnum);
126     return NULL;
127   }
128   if((numb = PySequence_Length($input)) != 0) {
129     $1 = (gpgme_key_t*)malloc((numb+1)*sizeof(gpgme_key_t));
130     for(i=0; i<numb; i++) {
131       PyObject *pypointer = PySequence_GetItem($input, i);
132
133       /* input = $input, 1 = $1, 1_descriptor = $1_descriptor */
134       /* &1_descriptor = $&1_descriptor *1_descriptor = $*1_descriptor */
135
136       /* Following code is from swig's python.swg.  */
137       if ((SWIG_ConvertPtr(pypointer,(void **) &$1[i], $*1_descriptor,SWIG_POINTER_EXCEPTION | $disown )) == -1) {
138         Py_DECREF(pypointer);
139         return NULL;
140       }
141       Py_DECREF(pypointer);
142     }
143     $1[numb] = NULL;
144   }
145 }
146 %typemap(freearg) gpgme_key_t [] {
147   if ($1) free($1);
148 }
149
150 /* Special handling for references to our objects.  */
151 %typemap(in) gpgme_data_t DATAIN (gpgme_data_t wrapper = NULL,
152                                   PyObject *bytesio = NULL,
153                                   Py_buffer view, int have_view = 0) {
154   /* If we create a temporary wrapper object, we will store it in
155      wrapperN, where N is $argnum.  Here in this fragment, SWIG will
156      automatically append $argnum.  */
157   memset(&view, 0, sizeof view);
158   if ($input == Py_None)
159     $1 = NULL;
160   else {
161     PyObject *pypointer;
162     pypointer = _pyme_obj2gpgme_data_t($input, $argnum, &wrapper,
163                                        &bytesio, &view);
164     if (pypointer == NULL)
165       return NULL;
166     have_view = !! view.obj;
167
168     /* input = $input, 1 = $1, 1_descriptor = $1_descriptor */
169
170     /* Following code is from swig's python.swg.  */
171
172     if ((SWIG_ConvertPtr(pypointer,(void **) &$1, $1_descriptor,
173          SWIG_POINTER_EXCEPTION | $disown )) == -1) {
174       Py_DECREF(pypointer);
175       return NULL;
176     }
177     Py_DECREF(pypointer);
178   }
179 }
180
181 #if HAVE_DATA_H
182 /* If we are doing an in-tree build, we can use the internal
183    representation of struct gpgme_data for an very efficient check if
184    the buffer has been modified.  */
185 %{
186 #include "src/data.h"   /* For struct gpgme_data.  */
187 %}
188 #endif
189
190 %typemap(freearg) gpgme_data_t DATAIN {
191   /* See whether we need to update the Python buffer.  */
192   if (resultobj && wrapper$argnum && view$argnum.buf)
193     {
194       int dirty;
195       char *new_data = NULL;
196       size_t new_size;
197
198 #if HAVE_DATA_H
199       new_data = wrapper$argnum->data.mem.buffer;
200       new_size = wrapper$argnum->data.mem.length;
201       dirty = new_data != NULL;
202 #else
203       new_data = gpgme_data_release_and_get_mem (wrapper$argnum, &new_size);
204       wrapper$argnum = NULL;
205       dirty = new_size != view$argnum.len
206         || memcmp (new_data, view$argnum.buf, view$argnum.len);
207 #endif
208
209       if (dirty)
210         {
211           /* The buffer is dirty.  */
212           if (view$argnum.readonly)
213             {
214               Py_XDECREF(resultobj);
215               resultobj = NULL;
216               PyErr_SetString(PyExc_ValueError,
217                               "cannot update read-only buffer");
218             }
219
220           /* See if we need to truncate the buffer.  */
221           if (resultobj && view$argnum.len != new_size)
222             {
223               if (bytesio$argnum == NULL)
224                 {
225                   Py_XDECREF(resultobj);
226                   resultobj = NULL;
227                   PyErr_SetString(PyExc_ValueError, "cannot resize buffer");
228                 }
229               else
230                 {
231                   PyObject *retval;
232                   PyBuffer_Release(&view$argnum);
233                   assert(view$argnum.obj == NULL);
234                   retval = PyObject_CallMethod(bytesio$argnum, "truncate",
235                                                "l", (long) new_size);
236                   if (retval == NULL)
237                     {
238                       Py_XDECREF(resultobj);
239                       resultobj = NULL;
240                     }
241                   else
242                     {
243                       Py_DECREF(retval);
244
245                       retval = PyObject_CallMethod(bytesio$argnum,
246                                                    "getbuffer", NULL);
247                       if (retval == NULL
248                           || PyObject_GetBuffer(retval, &view$argnum,
249                                            PyBUF_SIMPLE|PyBUF_WRITABLE) < 0)
250                         {
251                           Py_XDECREF(resultobj);
252                           resultobj = NULL;
253                         }
254
255                       Py_XDECREF(retval);
256
257                       if (resultobj && view$argnum.len
258                           != new_size)
259                         {
260                           Py_XDECREF(resultobj);
261                           resultobj = NULL;
262                           PyErr_Format(PyExc_ValueError,
263                                        "Expected buffer of length %zu, got %zi",
264                                        new_size,
265                                        view$argnum.len);
266                         }
267                     }
268                 }
269             }
270           if (resultobj)
271             memcpy(view$argnum.buf, new_data, new_size);
272         }
273 #if ! HAVE_DATA_H
274       free (new_data);
275 #endif
276     }
277
278   /* Free the temporary wrapper, if any.  */
279   if (wrapper$argnum)
280     gpgme_data_release(wrapper$argnum);
281   Py_XDECREF (bytesio$argnum);
282   if (have_view$argnum && view$argnum.buf)
283     PyBuffer_Release(&view$argnum);
284 }
285
286 %apply gpgme_data_t DATAIN {gpgme_data_t plain, gpgme_data_t cipher,
287                         gpgme_data_t sig, gpgme_data_t signed_text,
288                         gpgme_data_t plaintext, gpgme_data_t keydata,
289                         gpgme_data_t pubkey, gpgme_data_t seckey,
290                         gpgme_data_t out};
291
292 /* SWIG has problems interpreting ssize_t, off_t or gpgme_error_t in
293    gpgme.h.  */
294 %typemap(out) ssize_t, gpgme_error_t, gpgme_err_code_t, gpgme_err_source_t, gpg_error_t {
295   $result = PyLong_FromLong($1);
296 }
297
298 %typemap(in) ssize_t, gpgme_error_t, gpgme_err_code_t, gpgme_err_source_t, gpg_error_t {
299   if (PyLong_Check($input))
300     $1 = PyLong_AsLong($input);
301 #if PY_MAJOR_VERSION < 3
302   else if (PyInt_Check($input))
303     $1 = PyInt_AsLong($input);
304 #endif
305   else
306     PyErr_SetString(PyExc_TypeError, "Numeric argument expected");
307 }
308
309 %typemap(out) off_t {
310 #if _FILE_OFFSET_BITS == 64
311   $result = PyLong_FromLongLong($1);
312 #else
313   $result = PyLong_FromLong($1);
314 #endif
315 }
316
317 %typemap(in) off_t {
318   if (PyLong_Check($input))
319 #if _FILE_OFFSET_BITS == 64
320     $1 = PyLong_AsLongLong($input);
321 #else
322     $1 = PyLong_AsLong($input);
323 #endif
324 #if PY_MAJOR_VERSION < 3
325   else if (PyInt_Check($input))
326     $1 = PyInt_AsLong($input);
327 #endif
328   else
329     PyErr_SetString(PyExc_TypeError, "Numeric argument expected");
330 }
331
332 /* Those are for gpgme_data_read() and gpgme_strerror_r().  */
333 %typemap(in) (void *buffer, size_t size), (char *buf, size_t buflen) {
334   {
335     long tmp$argnum;
336     if (PyLong_Check($input))
337       tmp$argnum = PyLong_AsLong($input);
338 #if PY_MAJOR_VERSION < 3
339     else if (PyInt_Check($input))
340       tmp$argnum = PyInt_AsLong($input);
341 #endif
342     else
343       {
344         PyErr_SetString(PyExc_TypeError, "Numeric argument expected");
345         return NULL;
346       }
347
348     if (tmp$argnum < 0) {
349       PyErr_SetString(PyExc_ValueError, "Positive integer expected");
350       return NULL;
351     }
352     $2 = (size_t) tmp$argnum;
353     $1 = ($1_ltype) malloc($2+1);
354   }
355 }
356 %typemap(argout) (void *buffer, size_t size), (char *buf, size_t buflen) {
357   Py_XDECREF($result);   /* Blow away any previous result */
358   if (result < 0) {      /* Check for I/O error */
359     free($1);
360     return PyErr_SetFromErrno(PyExc_RuntimeError);
361   }
362   $result = PyBytes_FromStringAndSize($1,result);
363   free($1);
364 }
365
366 /* For gpgme_data_write, but should be universal.  */
367 %typemap(in) (const void *buffer, size_t size)(PyObject *encodedInput = NULL) {
368   Py_ssize_t ssize;
369
370   if ($input == Py_None)
371     $1 = NULL, $2 = 0;
372   else if (PyUnicode_Check($input))
373     {
374       encodedInput = PyUnicode_AsUTF8String($input);
375       if (encodedInput == NULL)
376         return NULL;
377       if (PyBytes_AsStringAndSize(encodedInput, (char **) &$1, &ssize) == -1)
378         {
379           Py_DECREF(encodedInput);
380           return NULL;
381         }
382     }
383   else if (PyBytes_Check($input))
384     PyBytes_AsStringAndSize($input, (char **) &$1, &ssize);
385   else {
386     PyErr_Format(PyExc_TypeError,
387                  "arg %d: expected str, bytes, or None, got %s",
388                  $argnum, $input->ob_type->tp_name);
389     return NULL;
390   }
391
392   if (! $1)
393     $2 = 0;
394   else
395     {
396       assert (ssize >= 0);
397       $2 = (size_t) ssize;
398     }
399 }
400 %typemap(freearg) (const void *buffer, size_t size) {
401   Py_XDECREF(encodedInput$argnum);
402 }
403
404 /* Make types containing 'next' field to be lists.  */
405 %ignore next;
406 %typemap(out) gpgme_sig_notation_t, gpgme_subkey_t,
407    gpgme_key_sig_t, gpgme_user_id_t, gpgme_invalid_key_t,
408    gpgme_recipient_t, gpgme_new_signature_t, gpgme_signature_t,
409    gpgme_import_status_t, gpgme_conf_arg_t, gpgme_conf_opt_t,
410    gpgme_conf_comp_t, gpgme_tofu_info_t {
411   int i;
412   int size = 0;
413   $1_ltype curr;
414   for (curr = $1; curr != NULL; curr = curr->next) {
415     size++;
416   }
417   $result = PyList_New(size);
418   for (i=0,curr=$1; i<size; i++,curr=curr->next) {
419     PyObject *o = SWIG_NewPointerObj(SWIG_as_voidptr(curr), $1_descriptor, %newpointer_flags);
420     PyList_SetItem($result, i, o);
421   }
422 }
423
424 \f
425
426 /* Wrap the fragile result objects into robust Python ones.  */
427 %typemap(out) gpgme_encrypt_result_t {
428   PyObject *fragile;
429   fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor,
430                                %newpointer_flags);
431   $result = _pyme_wrap_result(fragile, "EncryptResult");
432   Py_DECREF(fragile);
433 }
434
435 %typemap(out) gpgme_decrypt_result_t {
436   PyObject *fragile;
437   fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor,
438                                %newpointer_flags);
439   $result = _pyme_wrap_result(fragile, "DecryptResult");
440   Py_DECREF(fragile);
441 }
442
443 %typemap(out) gpgme_sign_result_t {
444   PyObject *fragile;
445   fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor,
446                                %newpointer_flags);
447   $result = _pyme_wrap_result(fragile, "SignResult");
448   Py_DECREF(fragile);
449 }
450
451 %typemap(out) gpgme_verify_result_t {
452   PyObject *fragile;
453   fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor,
454                                %newpointer_flags);
455   $result = _pyme_wrap_result(fragile, "VerifyResult");
456   Py_DECREF(fragile);
457 }
458
459 %typemap(out) gpgme_import_result_t {
460   PyObject *fragile;
461   fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor,
462                                %newpointer_flags);
463   $result = _pyme_wrap_result(fragile, "ImportResult");
464   Py_DECREF(fragile);
465 }
466
467 %typemap(out) gpgme_genkey_result_t {
468   PyObject *fragile;
469   fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor,
470                                %newpointer_flags);
471   $result = _pyme_wrap_result(fragile, "GenkeyResult");
472   Py_DECREF(fragile);
473 }
474
475 %typemap(out) gpgme_keylist_result_t {
476   PyObject *fragile;
477   fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor,
478                                %newpointer_flags);
479   $result = _pyme_wrap_result(fragile, "KeylistResult");
480   Py_DECREF(fragile);
481 }
482
483 %typemap(out) gpgme_vfs_mount_result_t {
484   PyObject *fragile;
485   fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor,
486                                %newpointer_flags);
487   $result = _pyme_wrap_result(fragile, "VFSMountResult");
488   Py_DECREF(fragile);
489 }
490
491 %typemap(out) gpgme_engine_info_t {
492   int i;
493   int size = 0;
494   $1_ltype curr;
495   for (curr = $1; curr != NULL; curr = curr->next) {
496     size++;
497   }
498   $result = PyList_New(size);
499   if ($result == NULL)
500     return NULL;        /* raise */
501   for (i=0,curr=$1; i<size; i++,curr=curr->next) {
502     PyObject *fragile, *o;
503     fragile = SWIG_NewPointerObj(SWIG_as_voidptr(curr), $1_descriptor,
504                                  %newpointer_flags);
505     if (fragile == NULL)
506       {
507         Py_DECREF($result);
508         return NULL;    /* raise */
509       }
510     o = _pyme_wrap_result(fragile, "EngineInfo");
511     Py_DECREF(fragile);
512     if (o == NULL)
513       {
514         Py_DECREF($result);
515         return NULL;    /* raise */
516       }
517     PyList_SetItem($result, i, o);
518   }
519 }
520
521 \f
522
523 /* Include mapper for interact callbacks.  */
524 %typemap(in) (gpgme_interact_cb_t fnc, void *fnc_value) {
525   if (! PyTuple_Check($input))
526     return PyErr_Format(PyExc_TypeError, "interact callback must be a tuple");
527   if (PyTuple_Size($input) != 2 && PyTuple_Size($input) != 3)
528     return PyErr_Format(PyExc_TypeError,
529                         "interact callback must be a tuple of size 2 or 3");
530
531   $1 = (gpgme_interact_cb_t) _pyme_interact_cb;
532   $2 = $input;
533 }
534
535 \f
536
537 /* The assuan protocol callbacks.  */
538 %typemap(in) (gpgme_assuan_data_cb_t data_cb, void *data_cb_value) {
539   if ($input == Py_None)
540     $1 = $2 = NULL;
541   else
542     {
543       if (! PyTuple_Check($input))
544         return PyErr_Format(PyExc_TypeError, "callback must be a tuple");
545       if (PyTuple_Size($input) != 2)
546         return PyErr_Format(PyExc_TypeError,
547                             "callback must be a tuple of size 2");
548       if (! PyCallable_Check(PyTuple_GetItem($input, 1)))
549         return PyErr_Format(PyExc_TypeError, "second item must be callable");
550       $1 = _pyme_assuan_data_cb;
551       $2 = $input;
552     }
553 }
554
555 %typemap(in) (gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value) {
556   if ($input == Py_None)
557     $1 = $2 = NULL;
558   else
559     {
560       if (! PyTuple_Check($input))
561         return PyErr_Format(PyExc_TypeError, "callback must be a tuple");
562       if (PyTuple_Size($input) != 2)
563         return PyErr_Format(PyExc_TypeError,
564                             "callback must be a tuple of size 2");
565       if (! PyCallable_Check(PyTuple_GetItem($input, 1)))
566         return PyErr_Format(PyExc_TypeError, "second item must be callable");
567       $1 = _pyme_assuan_inquire_cb;
568       $2 = $input;
569     }
570 }
571
572 %typemap(in) (gpgme_assuan_status_cb_t stat_cb, void *stat_cb_value) {
573   if ($input == Py_None)
574     $1 = $2 = NULL;
575   else
576     {
577       if (! PyTuple_Check($input))
578         return PyErr_Format(PyExc_TypeError, "callback must be a tuple");
579       if (PyTuple_Size($input) != 2)
580         return PyErr_Format(PyExc_TypeError,
581                             "callback must be a tuple of size 2");
582       if (! PyCallable_Check(PyTuple_GetItem($input, 1)))
583         return PyErr_Format(PyExc_TypeError, "second item must be callable");
584       $1 = _pyme_assuan_status_cb;
585       $2 = $input;
586     }
587 }
588
589 /* Include the unmodified <gpgme.h> for cc, and the cleaned-up local
590    version for SWIG.  We do, however, want to hide certain fields on
591    some structs, which we provide prior to including the version for
592    SWIG.  */
593 %{
594 #ifdef HAVE_CONFIG_H
595 #include "config.h"
596 #endif
597
598 #include <gpgme.h>
599 %}
600
601 /* This is for notations, where we want to hide the length fields, and
602    the unused bit field block.  */
603 struct _gpgme_sig_notation
604 {
605   struct _gpgme_sig_notation *next;
606
607   /* If NAME is a null pointer, then VALUE contains a policy URL
608      rather than a notation.  */
609   char *name;
610
611   /* The value of the notation data.  */
612   char *value;
613
614   /* The accumulated flags.  */
615   gpgme_sig_notation_flags_t flags;
616
617   /* Notation data is human-readable.  */
618   unsigned int human_readable : 1;
619
620   /* Notation data is critical.  */
621   unsigned int critical : 1;
622 };
623
624 /* Now include our local modified version.  Any structs defined above
625    are ignored.  */
626 #ifdef HAVE_CONFIG_H
627 %include "config.h"
628 #endif
629
630 %include "gpgme.h"
631
632 %include "errors.i"
633
634 /* Generating and handling pointers-to-pointers.  */
635
636 %pointer_functions(gpgme_ctx_t, gpgme_ctx_t_p);
637 %pointer_functions(gpgme_data_t, gpgme_data_t_p);
638 %pointer_functions(gpgme_key_t, gpgme_key_t_p);
639 %pointer_functions(gpgme_error_t, gpgme_error_t_p);
640 %pointer_functions(gpgme_trust_item_t, gpgme_trust_item_t_p);
641 %pointer_functions(gpgme_engine_info_t, gpgme_engine_info_t_p);
642
643 /* Helper functions.  */
644
645 %{
646 #include <stdio.h>
647 %}
648 FILE *fdopen(int fildes, const char *mode);
649
650 /* We include both headers in the generated c code...  */
651 %{
652 #include "helpers.h"
653 #include "private.h"
654
655 /* SWIG runtime support for helpers.c  */
656 PyObject *
657 _pyme_wrap_gpgme_data_t(gpgme_data_t data)
658 {
659   return SWIG_Python_NewPointerObj(NULL, data, SWIGTYPE_p_gpgme_data, 0);
660 }
661
662 gpgme_ctx_t
663 _pyme_unwrap_gpgme_ctx_t(PyObject *wrapped)
664 {
665   gpgme_ctx_t result;
666   if (SWIG_ConvertPtr(wrapped,
667                       (void **) &result,
668                       SWIGTYPE_p_gpgme_context,
669                       SWIG_POINTER_EXCEPTION) == -1)
670     return NULL;
671   return result;
672 }
673 %}
674
675 /* ... but only the public definitions here.  They will be exposed to
676    the Python world, so let's be careful.  */
677 %include "helpers.h"