python: Rework callbacks.
[gpgme.git] / lang / python / pyme / core.py
1 # Copyright (C) 2016 g10 Code GmbH
2 # Copyright (C) 2004,2008 Igor Belyi <belyi@users.sourceforge.net>
3 # Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
4 #
5 #    This library is free software; you can redistribute it and/or
6 #    modify it under the terms of the GNU Lesser General Public
7 #    License as published by the Free Software Foundation; either
8 #    version 2.1 of the License, or (at your option) any later version.
9 #
10 #    This library is distributed in the hope that it will be useful,
11 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 #    Lesser General Public License for more details.
14 #
15 #    You should have received a copy of the GNU Lesser General Public
16 #    License along with this library; if not, write to the Free Software
17 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
18
19 """Core functionality
20
21 Core functionality of GPGME wrapped in a object-oriented fashion.
22 Provides the 'Context' class for performing cryptographic operations,
23 and the 'Data' class describing buffers of data.
24
25 """
26
27 import weakref
28 from . import pygpgme
29 from .errors import errorcheck, GPGMEError
30 from . import constants
31 from . import errors
32
33 class GpgmeWrapper(object):
34     """Base wrapper class
35
36     Not to be instantiated directly.
37
38     """
39
40     def __init__(self, wrapped):
41         self._callback_excinfo = None
42         self.wrapped = wrapped
43
44     def __repr__(self):
45         return '<{}/{!r}>'.format(super().__repr__(), self.wrapped)
46
47     def __str__(self):
48         acc = ['{}.{}'.format(__name__, self.__class__.__name__)]
49         flags = [f for f in self._boolean_properties if getattr(self, f)]
50         if flags:
51             acc.append('({})'.format(' '.join(flags)))
52
53         return '<{}>'.format(' '.join(acc))
54
55     def __hash__(self):
56         return hash(repr(self.wrapped))
57
58     def __eq__(self, other):
59         if other == None:
60             return False
61         else:
62             return repr(self.wrapped) == repr(other.wrapped)
63
64     def _getctype(self):
65         """Must be implemented by child classes.
66
67         Must return the name of the c type."""
68         raise NotImplementedError()
69
70     def _getnameprepend(self):
71         """Must be implemented by child classes.
72
73         Must return the prefix of all c functions mapped to methods of
74         this class."""
75         raise NotImplementedError()
76
77     def _errorcheck(self, name):
78         """Must be implemented by child classes.
79
80         This function must return a trueish value for all c functions
81         returning gpgme_error_t."""
82         raise NotImplementedError()
83
84     """The set of all boolean properties"""
85     _boolean_properties = set()
86
87     def __wrap_boolean_property(self, key, do_set=False, value=None):
88         get_func = getattr(pygpgme,
89                            "{}get_{}".format(self._getnameprepend(), key))
90         set_func = getattr(pygpgme,
91                            "{}set_{}".format(self._getnameprepend(), key))
92         def get(slf):
93             return bool(get_func(slf.wrapped))
94         def set_(slf, value):
95             set_func(slf.wrapped, bool(value))
96
97         p = property(get, set_, doc="{} flag".format(key))
98         setattr(self.__class__, key, p)
99
100         if do_set:
101             set_(self, bool(value))
102         else:
103             return get(self)
104
105     def __getattr__(self, key):
106         """On-the-fly generation of wrapper methods and properties"""
107         if key[0] == '_' or self._getnameprepend() == None:
108             return None
109
110         if key in self._boolean_properties:
111             return self.__wrap_boolean_property(key)
112
113         name = self._getnameprepend() + key
114         func = getattr(pygpgme, name)
115
116         if self._errorcheck(name):
117             def _funcwrap(slf, *args, **kwargs):
118                 result = func(slf.wrapped, *args, **kwargs)
119                 if slf._callback_excinfo:
120                     pygpgme.pygpgme_raise_callback_exception(slf)
121                 return errorcheck(result, "Invocation of " + name)
122         else:
123             def _funcwrap(slf, *args, **kwargs):
124                 result = func(slf.wrapped, *args, **kwargs)
125                 if slf._callback_excinfo:
126                     pygpgme.pygpgme_raise_callback_exception(slf)
127                 return result
128
129         _funcwrap.__doc__ = getattr(func, "__doc__")
130
131         # Monkey-patch the class.
132         setattr(self.__class__, key, _funcwrap)
133
134         # Bind the method to 'self'.
135         def wrapper(*args, **kwargs):
136             return _funcwrap(self, *args, **kwargs)
137         _funcwrap.__doc__ = getattr(func, "__doc__")
138
139         return wrapper
140
141     def __setattr__(self, key, value):
142         """On-the-fly generation of properties"""
143         if key in self._boolean_properties:
144             self.__wrap_boolean_property(key, True, value)
145         else:
146             super().__setattr__(key, value)
147
148 class Context(GpgmeWrapper):
149     """Context for cryptographic operations
150
151     All cryptographic operations in GPGME are performed within a
152     context, which contains the internal state of the operation as
153     well as configuration parameters.  By using several contexts you
154     can run several cryptographic operations in parallel, with
155     different configuration.
156
157     Access to a context must be synchronized.
158
159     """
160
161     @property
162     def signers(self):
163         """Keys used for signing"""
164         return [self.signers_enum(i) for i in range(self.signers_count())]
165     @signers.setter
166     def signers(self, signers):
167         old = self.signers
168         self.signers_clear()
169         try:
170             for key in signers:
171                 self.signers_add(key)
172         except:
173             self.signers = old
174             raise
175
176     @property
177     def pinentry_mode(self):
178         """Pinentry mode"""
179         return self.get_pinentry_mode()
180     @pinentry_mode.setter
181     def pinentry_mode(self, value):
182         self.set_pinentry_mode(value)
183
184     def _getctype(self):
185         return 'gpgme_ctx_t'
186
187     def _getnameprepend(self):
188         return 'gpgme_'
189
190     def _errorcheck(self, name):
191         """This function should list all functions returning gpgme_error_t"""
192         if (name.startswith('gpgme_op_') and \
193             not name.endswith('_result')) or \
194             name == 'gpgme_signers_add' or \
195             name == 'gpgme_set_locale' or \
196             name == 'gpgme_set_keylist_mode' or \
197             name == 'gpgme_set_protocol':
198             return 1
199         return 0
200
201     _boolean_properties = {'armor', 'textmode', 'offline'}
202     def __init__(self, armor=False, textmode=False, offline=False,
203                  signers=[], pinentry_mode=constants.PINENTRY_MODE_DEFAULT,
204                  wrapped=None):
205         """Construct a context object
206
207         Keyword arguments:
208         armor           -- enable ASCII armoring (default False)
209         textmode        -- enable canonical text mode (default False)
210         offline         -- do not contact external key sources (default False)
211         signers         -- list of keys used for signing (default [])
212         pinentry_mode   -- pinentry mode (default PINENTRY_MODE_DEFAULT)
213         """
214         if wrapped:
215             self.own = False
216         else:
217             tmp = pygpgme.new_gpgme_ctx_t_p()
218             errorcheck(pygpgme.gpgme_new(tmp))
219             wrapped = pygpgme.gpgme_ctx_t_p_value(tmp)
220             pygpgme.delete_gpgme_ctx_t_p(tmp)
221             self.own = True
222         super().__init__(wrapped)
223         self.armor = armor
224         self.textmode = textmode
225         self.offline = offline
226         self.signers = signers
227         self.pinentry_mode = pinentry_mode
228
229     def __del__(self):
230         if not pygpgme:
231             # At interpreter shutdown, pygpgme is set to NONE.
232             return
233
234         self._free_passcb()
235         self._free_progresscb()
236         self._free_statuscb()
237         if self.own and self.wrapped and pygpgme.gpgme_release:
238             pygpgme.gpgme_release(self.wrapped)
239             self.wrapped = None
240
241     # Implement the context manager protocol.
242     def __enter__(self):
243         return self
244     def __exit__(self, type, value, tb):
245         self.__del__()
246
247     def op_keylist_all(self, *args, **kwargs):
248         self.op_keylist_start(*args, **kwargs)
249         key = self.op_keylist_next()
250         while key:
251             yield key
252             key = self.op_keylist_next()
253
254     def op_keylist_next(self):
255         """Returns the next key in the list created
256         by a call to op_keylist_start().  The object returned
257         is of type Key."""
258         ptr = pygpgme.new_gpgme_key_t_p()
259         try:
260             errorcheck(pygpgme.gpgme_op_keylist_next(self.wrapped, ptr))
261             key = pygpgme.gpgme_key_t_p_value(ptr)
262         except errors.GPGMEError as excp:
263             key = None
264             if excp.getcode() != errors.EOF:
265                 raise excp
266         pygpgme.delete_gpgme_key_t_p(ptr)
267         if key:
268             key.__del__ = lambda self: pygpgme.gpgme_key_unref(self)
269             return key
270
271     def get_key(self, fpr, secret):
272         """Return the key corresponding to the fingerprint 'fpr'"""
273         ptr = pygpgme.new_gpgme_key_t_p()
274         errorcheck(pygpgme.gpgme_get_key(self.wrapped, fpr, ptr, secret))
275         key = pygpgme.gpgme_key_t_p_value(ptr)
276         pygpgme.delete_gpgme_key_t_p(ptr)
277         if key:
278             key.__del__ = lambda self: pygpgme.gpgme_key_unref(self)
279             return key
280
281     def op_trustlist_all(self, *args, **kwargs):
282         self.op_trustlist_start(*args, **kwargs)
283         trust = self.ctx.op_trustlist_next()
284         while trust:
285             yield trust
286             trust = self.ctx.op_trustlist_next()
287
288     def op_trustlist_next(self):
289         """Returns the next trust item in the list created
290         by a call to op_trustlist_start().  The object returned
291         is of type TrustItem."""
292         ptr = pygpgme.new_gpgme_trust_item_t_p()
293         try:
294             errorcheck(pygpgme.gpgme_op_trustlist_next(self.wrapped, ptr))
295             trust = pygpgme.gpgme_trust_item_t_p_value(ptr)
296         except errors.GPGMEError as excp:
297             trust = None
298             if excp.getcode() != errors.EOF:
299                 raise
300         pygpgme.delete_gpgme_trust_item_t_p(ptr)
301         return trust
302
303     def set_passphrase_cb(self, func, hook=None):
304         """Sets the passphrase callback to the function specified by func.
305
306         When the system needs a passphrase, it will call func with three args:
307         hint, a string describing the key it needs the passphrase for;
308         desc, a string describing the passphrase it needs;
309         prev_bad, a boolean equal True if this is a call made after
310         unsuccessful previous attempt.
311
312         If hook has a value other than None it will be passed into the func
313         as a forth argument.
314
315         Please see the GPGME manual for more information.
316         """
317         if func == None:
318             hookdata = None
319         else:
320             if hook == None:
321                 hookdata = (weakref.ref(self), func)
322             else:
323                 hookdata = (weakref.ref(self), func, hook)
324         pygpgme.pygpgme_set_passphrase_cb(self, hookdata)
325
326     def _free_passcb(self):
327         if pygpgme.pygpgme_set_passphrase_cb:
328             self.set_passphrase_cb(None)
329
330     def set_progress_cb(self, func, hook=None):
331         """Sets the progress meter callback to the function specified by FUNC.
332         If FUNC is None, the callback will be cleared.
333
334         This function will be called to provide an interactive update
335         of the system's progress.  The function will be called with
336         three arguments, type, total, and current.  If HOOK is not
337         None, it will be supplied as fourth argument.
338
339         Please see the GPGME manual for more information.
340
341         """
342         if func == None:
343             hookdata = None
344         else:
345             if hook == None:
346                 hookdata = (weakref.ref(self), func)
347             else:
348                 hookdata = (weakref.ref(self), func, hook)
349         pygpgme.pygpgme_set_progress_cb(self, hookdata)
350
351     def _free_progresscb(self):
352         if pygpgme.pygpgme_set_progress_cb:
353             self.set_progress_cb(None)
354
355     def set_status_cb(self, func, hook=None):
356         """Sets the status callback to the function specified by FUNC.  If
357         FUNC is None, the callback will be cleared.
358
359         The function will be called with two arguments, keyword and
360         args.  If HOOK is not None, it will be supplied as third
361         argument.
362
363         Please see the GPGME manual for more information.
364
365         """
366         if func == None:
367             hookdata = None
368         else:
369             if hook == None:
370                 hookdata = (weakref.ref(self), func)
371             else:
372                 hookdata = (weakref.ref(self), func, hook)
373         pygpgme.pygpgme_set_status_cb(self, hookdata)
374
375     def _free_statuscb(self):
376         if pygpgme.pygpgme_set_status_cb:
377             self.set_status_cb(None)
378
379     def get_engine_info(self):
380         """Returns this context specific engine info"""
381         return pygpgme.gpgme_ctx_get_engine_info(self.wrapped)
382
383     def set_engine_info(self, proto, file_name, home_dir=None):
384         """Changes the configuration of the crypto engine implementing the
385     protocol 'proto' for the context. 'file_name' is the file name of
386     the executable program implementing this protocol. 'home_dir' is the
387     directory name of the configuration directory (engine's default is
388     used if omitted)."""
389         errorcheck(pygpgme.gpgme_ctx_set_engine_info(self.wrapped, proto, file_name, home_dir))
390
391     def wait(self, hang):
392         """Wait for asynchronous call to finish. Wait forever if hang is True.
393         Raises an exception on errors.
394
395         Please read the GPGME manual for more information.
396
397         """
398         ptr = pygpgme.new_gpgme_error_t_p()
399         pygpgme.gpgme_wait(self.wrapped, ptr, hang)
400         status = pygpgme.gpgme_error_t_p_value(ptr)
401         pygpgme.delete_gpgme_error_t_p(ptr)
402         errorcheck(status)
403
404     def op_edit(self, key, func, fnc_value, out):
405         """Start key editing using supplied callback function"""
406         if key == None:
407             raise ValueError("op_edit: First argument cannot be None")
408         if fnc_value:
409             opaquedata = (weakref.ref(self), func, fnc_value)
410         else:
411             opaquedata = (weakref.ref(self), func)
412
413         result = pygpgme.gpgme_op_edit(self.wrapped, key, opaquedata, out)
414         if self._callback_excinfo:
415             pygpgme.pygpgme_raise_callback_exception(self)
416         errorcheck(result)
417
418 class Data(GpgmeWrapper):
419     """Data buffer
420
421     A lot of data has to be exchanged between the user and the crypto
422     engine, like plaintext messages, ciphertext, signatures and
423     information about the keys.  The technical details about
424     exchanging the data information are completely abstracted by
425     GPGME.  The user provides and receives the data via `gpgme_data_t'
426     objects, regardless of the communication protocol between GPGME
427     and the crypto engine in use.
428
429     This Data class is the implementation of the GpgmeData objects.
430
431     Please see the information about __init__ for instantiation.
432
433     """
434
435     def _getctype(self):
436         return 'gpgme_data_t'
437
438     def _getnameprepend(self):
439         return 'gpgme_data_'
440
441     def _errorcheck(self, name):
442         """This function should list all functions returning gpgme_error_t"""
443         return name not in {
444             'gpgme_data_release_and_get_mem',
445             'gpgme_data_get_encoding',
446             'gpgme_data_seek',
447             'gpgme_data_get_file_name',
448         }
449
450     def __init__(self, string=None, file=None, offset=None,
451                  length=None, cbs=None, copy=True):
452         """Initialize a new gpgme_data_t object.
453
454         If no args are specified, make it an empty object.
455
456         If string alone is specified, initialize it with the data
457         contained there.
458
459         If file, offset, and length are all specified, file must
460         be either a filename or a file-like object, and the object
461         will be initialized by reading the specified chunk from the file.
462
463         If cbs is specified, it MUST be a tuple of the form:
464
465         (read_cb, write_cb, seek_cb, release_cb[, hook])
466
467         where the first four items are functions implementing reading,
468         writing, seeking the data, and releasing any resources once
469         the data object is deallocated.  The functions must match the
470         following prototypes:
471
472             def read(amount, hook=None):
473                 return <a b"bytes" object>
474
475             def write(data, hook=None):
476                 return <the number of bytes written>
477
478             def seek(offset, whence, hook=None):
479                 return <the new file position>
480
481             def release(hook=None):
482                 <return value and exceptions are ignored>
483
484         The functions may be bound methods.  In that case, you can
485         simply use the 'self' reference instead of using a hook.
486
487         If file is specified without any other arguments, then
488         it must be a filename, and the object will be initialized from
489         that file.
490
491         """
492         super().__init__(None)
493         self.data_cbs = None
494
495         if cbs != None:
496             self.new_from_cbs(*cbs)
497         elif string != None:
498             self.new_from_mem(string, copy)
499         elif file != None and offset != None and length != None:
500             self.new_from_filepart(file, offset, length)
501         elif file != None:
502             if type(file) == type("x"):
503                 self.new_from_file(file, copy)
504             else:
505                 self.new_from_fd(file)
506         else:
507             self.new()
508
509     def __del__(self):
510         if not pygpgme:
511             # At interpreter shutdown, pygpgme is set to NONE.
512             return
513
514         if self.wrapped != None and pygpgme.gpgme_data_release:
515             pygpgme.gpgme_data_release(self.wrapped)
516             if self._callback_excinfo:
517                 pygpgme.pygpgme_raise_callback_exception(self)
518             self.wrapped = None
519         self._free_datacbs()
520
521     # Implement the context manager protocol.
522     def __enter__(self):
523         return self
524     def __exit__(self, type, value, tb):
525         self.__del__()
526
527     def _free_datacbs(self):
528         self._data_cbs = None
529
530     def new(self):
531         tmp = pygpgme.new_gpgme_data_t_p()
532         errorcheck(pygpgme.gpgme_data_new(tmp))
533         self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
534         pygpgme.delete_gpgme_data_t_p(tmp)
535
536     def new_from_mem(self, string, copy=True):
537         tmp = pygpgme.new_gpgme_data_t_p()
538         errorcheck(pygpgme.gpgme_data_new_from_mem(tmp,string,len(string),copy))
539         self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
540         pygpgme.delete_gpgme_data_t_p(tmp)
541
542     def new_from_file(self, filename, copy=True):
543         tmp = pygpgme.new_gpgme_data_t_p()
544         try:
545             errorcheck(pygpgme.gpgme_data_new_from_file(tmp, filename, copy))
546         except errors.GPGMEError as e:
547             if e.getcode() == errors.INV_VALUE and not copy:
548                 raise ValueError("delayed reads are not yet supported")
549             else:
550                 raise e
551         self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
552         pygpgme.delete_gpgme_data_t_p(tmp)
553
554     def new_from_cbs(self, read_cb, write_cb, seek_cb, release_cb, hook=None):
555         tmp = pygpgme.new_gpgme_data_t_p()
556         if hook != None:
557             hookdata = (weakref.ref(self),
558                         read_cb, write_cb, seek_cb, release_cb, hook)
559         else:
560             hookdata = (weakref.ref(self),
561                         read_cb, write_cb, seek_cb, release_cb)
562         pygpgme.pygpgme_data_new_from_cbs(self, hookdata, tmp)
563         self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
564         pygpgme.delete_gpgme_data_t_p(tmp)
565
566     def new_from_filepart(self, file, offset, length):
567         """This wraps the GPGME gpgme_data_new_from_filepart() function.
568         The argument "file" may be:
569
570         * a string specifying a file name, or
571         * a file-like object supporting the fileno() and the mode attribute.
572
573         """
574
575         tmp = pygpgme.new_gpgme_data_t_p()
576         filename = None
577         fp = None
578
579         if type(file) == type("x"):
580             filename = file
581         else:
582             fp = pygpgme.fdopen(file.fileno(), file.mode)
583             if fp == None:
584                 raise ValueError("Failed to open file from %s arg %s" % \
585                       (str(type(file)), str(file)))
586
587         errorcheck(pygpgme.gpgme_data_new_from_filepart(tmp, filename, fp,
588                                                       offset, length))
589         self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
590         pygpgme.delete_gpgme_data_t_p(tmp)
591
592     def new_from_fd(self, file):
593         """This wraps the GPGME gpgme_data_new_from_fd() function.  The
594         argument "file" must be a file-like object, supporting the
595         fileno() method.
596
597         """
598         tmp = pygpgme.new_gpgme_data_t_p()
599         errorcheck(pygpgme.gpgme_data_new_from_fd(tmp, file.fileno()))
600         self.wrapped = pygpgme.gpgme_data_t_p_value(tmp)
601         pygpgme.delete_gpgme_data_t_p(tmp)
602
603     def new_from_stream(self, file):
604         """This wrap around gpgme_data_new_from_stream is an alias for
605         new_from_fd() method since in python there's not difference
606         between file stream and file descriptor"""
607         self.new_from_fd(file)
608
609     def write(self, buffer):
610         """Write buffer given as string or bytes.
611
612         If a string is given, it is implicitly encoded using UTF-8."""
613         written = pygpgme.gpgme_data_write(self.wrapped, buffer)
614         if written < 0:
615             if self._callback_excinfo:
616                 pygpgme.pygpgme_raise_callback_exception(self)
617             else:
618                 raise GPGMEError.fromSyserror()
619         return written
620
621     def read(self, size = -1):
622         """Read at most size bytes, returned as bytes.
623
624         If the size argument is negative or omitted, read until EOF is reached.
625
626         Returns the data read, or the empty string if there was no data
627         to read before EOF was reached."""
628
629         if size == 0:
630             return ''
631
632         if size > 0:
633             try:
634                 result = pygpgme.gpgme_data_read(self.wrapped, size)
635             except:
636                 if self._callback_excinfo:
637                     pygpgme.pygpgme_raise_callback_exception(self)
638                 else:
639                     raise
640             return result
641         else:
642             chunks = []
643             while True:
644                 try:
645                     result = pygpgme.gpgme_data_read(self.wrapped, 4096)
646                 except:
647                     if self._callback_excinfo:
648                         pygpgme.pygpgme_raise_callback_exception(self)
649                     else:
650                         raise
651                 if len(result) == 0:
652                     break
653                 chunks.append(result)
654             return b''.join(chunks)
655
656 def pubkey_algo_name(algo):
657     return pygpgme.gpgme_pubkey_algo_name(algo)
658
659 def hash_algo_name(algo):
660     return pygpgme.gpgme_hash_algo_name(algo)
661
662 def get_protocol_name(proto):
663     return pygpgme.gpgme_get_protocol_name(proto)
664
665 def check_version(version=None):
666     return pygpgme.gpgme_check_version(version)
667
668 # check_version also makes sure that several subsystems are properly
669 # initialized, and it must be run at least once before invoking any
670 # other function.  We do it here so that the user does not have to do
671 # it unless she really wants to check for a certain version.
672 check_version()
673
674 def engine_check_version (proto):
675     try:
676         errorcheck(pygpgme.gpgme_engine_check_version(proto))
677         return True
678     except errors.GPGMEError:
679         return False
680
681 def get_engine_info():
682     ptr = pygpgme.new_gpgme_engine_info_t_p()
683     try:
684         errorcheck(pygpgme.gpgme_get_engine_info(ptr))
685         info = pygpgme.gpgme_engine_info_t_p_value(ptr)
686     except errors.GPGMEError:
687         info = None
688     pygpgme.delete_gpgme_engine_info_t_p(ptr)
689     return info
690
691 def set_engine_info(proto, file_name, home_dir=None):
692     """Changes the default configuration of the crypto engine implementing
693     the protocol 'proto'. 'file_name' is the file name of
694     the executable program implementing this protocol. 'home_dir' is the
695     directory name of the configuration directory (engine's default is
696     used if omitted)."""
697     errorcheck(pygpgme.gpgme_set_engine_info(proto, file_name, home_dir))
698
699 def set_locale(category, value):
700     """Sets the default locale used by contexts"""
701     errorcheck(pygpgme.gpgme_set_locale(None, category, value))
702
703 def wait(hang):
704     """Wait for asynchronous call on any Context  to finish.
705     Wait forever if hang is True.
706
707     For finished anynch calls it returns a tuple (status, context):
708         status  - status return by asnynchronous call.
709         context - context which caused this call to return.
710
711     Please read the GPGME manual of more information."""
712     ptr = pygpgme.new_gpgme_error_t_p()
713     context = pygpgme.gpgme_wait(None, ptr, hang)
714     status = pygpgme.gpgme_error_t_p_value(ptr)
715     pygpgme.delete_gpgme_error_t_p(ptr)
716     if context == None:
717         errorcheck(status)
718     else:
719         context = Context(context)
720     return (status, context)