Skip to content

Commit 0600f91

Browse files
committed
cleanup for merge
2 parents 9fbf79c + 8f564c8 commit 0600f91

26 files changed

+1591
-1051
lines changed

ChangeLog

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
Changes in version 0.3.1 (10 April 2015)
2+
3+
* implemented asynchronous queries, bound to Context()
4+
objects, and introduced Context.run() method. See
5+
getdns.readthedocs.org
6+
7+
* queries now return a Result() object, with attributes.
8+
See getdns.readthedocs.org
9+
10+
* removed leading GETDNS_ in all constants
11+
(i.e. getdns.GETDNS_RRTYPE_AAAA is now getdns.RRTYPE_AAAA)
12+
13+
* added getdns.get_errorstr_by_id() method, making it easier
14+
to provide user-friendly error messages
15+
16+
* prettied up printing of canonical names in Result object
17+
18+
* str and repr printing has been added to both Context and
19+
Result objects
20+
21+
* dead code removed
22+
23+
* replaced instances of getdns_strerror() with
24+
getdns_get_errorstr_by_id()
25+
26+
* fixed incorrect error return from Result construction
27+
28+
* moved __version__ attribute from Context to top-level
29+
getdns module
30+
31+
* made exception handling within the module more consistent
32+
33+
* added documentation describing getdns.error
34+
35+
* broke out query types

context.c

Lines changed: 360 additions & 208 deletions
Large diffs are not rendered by default.

context_util.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#include <Python.h>
2+
#include <getdns/getdns.h>
3+
#include <arpa/inet.h>
4+
#include "pygetdns.h"
5+
6+
7+
/*
8+
* Get the address of the Python function object
9+
* being passed in by name to the context
10+
* query methods
11+
*/
12+
13+
PyObject *
14+
get_callback(char *py_main, char *callback)
15+
{
16+
PyObject *main_module;
17+
PyObject *main_dict;
18+
PyObject *callback_func;
19+
20+
if ((main_module = PyImport_AddModule(py_main)) == 0) {
21+
PyErr_SetString(getdns_error, "No 'main'");
22+
return NULL;
23+
}
24+
main_dict = PyModule_GetDict(main_module);
25+
if ((callback_func = PyDict_GetItemString(main_dict, callback)) == 0) {
26+
PyErr_SetString(getdns_error, "callback not found\n");
27+
return NULL;
28+
}
29+
if (!PyCallable_Check(callback_func)) {
30+
PyErr_SetString(getdns_error, "The callback function is not runnable");
31+
return NULL;
32+
}
33+
return callback_func;
34+
}
35+
36+
37+
void
38+
callback_shim(struct getdns_context *context,
39+
getdns_callback_type_t type,
40+
struct getdns_dict *response,
41+
void *userarg,
42+
getdns_transaction_t tid)
43+
{
44+
PyObject *py_callback_type;
45+
PyObject *py_result;
46+
PyObject *py_tid;
47+
PyObject *py_userarg;
48+
49+
userarg_blob *u = (userarg_blob *)userarg;
50+
if ((py_callback_type = PyInt_FromLong((long)type)) == NULL) {
51+
PyObject *err_type, *err_value, *err_traceback;
52+
PyErr_Fetch(&err_type, &err_value, &err_traceback);
53+
PyErr_Restore(err_type, err_value, err_traceback);
54+
return;
55+
}
56+
if (type == GETDNS_CALLBACK_CANCEL) {
57+
py_result = Py_None;
58+
py_tid = Py_None;
59+
py_userarg = Py_None;
60+
} else {
61+
py_result = result_create(response);
62+
py_tid = PyInt_FromLong((long)tid);
63+
if (u->userarg)
64+
py_userarg = PyString_FromString(u->userarg);
65+
else
66+
py_userarg = Py_None;
67+
}
68+
PyObject_CallFunctionObjArgs(u->callback_func, py_callback_type, py_result, py_userarg, py_tid, NULL);
69+
}

doc/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@
4848
# built documents.
4949
#
5050
# The short X.Y version.
51-
version = '0.2.1'
51+
version = '0.3.1'
5252
# The full version, including alpha/beta/rc tags.
53-
release = '0.2.1'
53+
release = '0.3.1'
5454

5555
# The language for content autogenerated by Sphinx. Refer to documentation
5656
# for a list of supported languages.

doc/exceptions.rst

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
:mod:`getdns` exceptions
2+
=============================
3+
4+
.. module:: getdns
5+
:synopsis: getdns exception description and explanation
6+
.. sectionauthor:: Melinda Shore <[email protected]>
7+
8+
9+
getdns exceptions
10+
-----------------
11+
12+
.. py:exception:: getdns.error
13+
14+
getdns will throw an exception, ``getdns.error``, under
15+
certain conditions. Those conditions include:
16+
17+
* a required parameter having a bad value
18+
* a badly-formed domain name in the query
19+
* a bad ``Context()`` object
20+
* a failed ``Context()`` update
21+
* an out-of-bounds error for a getdns data structure
22+
* requesting an extension that doesn't exist
23+
* requesting DNSSEC validation while using stub
24+
resolution
25+
26+
Please note that a successful return from a getdns method
27+
does `not` indicate that the query returned the records
28+
being requested, but rather that the query is formed
29+
correctly and has been submitted to the DNS. A getdns
30+
exception is typically the result of a coding error.
31+
32+
getdns will set the exception message to a diagnostic
33+
string, which may be examined for help in resolving the
34+
error.
35+
36+
Example
37+
-------
38+
39+
::
40+
41+
import getdns, sys
42+
43+
c = getdns.Context()
44+
try:
45+
results = c.address('www.example.com', foo='bar')
46+
except getdns.error, e:
47+
print(str(e))
48+
sys.exit(1)
49+
50+
51+
This will result in "A required parameter had an invalid
52+
value" being printed to the screen.

doc/functions.rst

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,3 +415,101 @@ call to the API. Each member has the following names:
415415
* ``end_time`` is the time the query was received in milliseconds since the epoch, represented as an integer
416416
* ``entire_reply`` is the entire response received
417417
* ``dnssec_result`` is the DNSSEC status, or ``getdns.GETDNS_DNSSEC_NOT_PERFORMED`` if DNSSEC validation was not performed
418+
419+
420+
Asynchronous queries
421+
^^^^^^^^^^^^^^^^^^^^
422+
423+
The getdns Python bindings support asynchronous queries, in
424+
which a query returns immediately and a callback function is
425+
invoked when the response data are returned. The query
426+
method interfaces are fundamentally the same, with a few
427+
differences:
428+
429+
* The query returns a transaction id. That transaction
430+
id may be used to cancel future callbacks
431+
* The query invocation includes the name of a callback
432+
function. For example, if you'd like to call the
433+
function "my_callback" when the query returns, an
434+
address lookup could look like
435+
436+
>>> c = getdns.Context()
437+
>>> tid = c.address('www.example.org', callback='my_callback')
438+
439+
* We've introduced a new ``Context`` method, called
440+
``run``. When your program is ready to check to see
441+
whether or not the query has returned, invoke the run()
442+
method on your context. Note that we use the libevent
443+
asynchronous event library and an event_base is
444+
associated with a context. So, if you have multiple
445+
outstanding events associated with a particular
446+
context, ``run`` will invoke all of those that are
447+
waiting and ready.
448+
449+
The callback script takes four arguments: ``type``,
450+
``result``, ``userarg``, and ``transaction_id. The ``type``
451+
argument contains the callback type, which may have one of
452+
the following values:
453+
454+
* ``getdns.CALLBACK_COMPLETE``: The query was successful
455+
and the results are contained in the ``result``
456+
argument
457+
* ``getdns.CALLBACK_CANCEL``: The callback was cancelled
458+
before the results were processed
459+
* ``getdns.CALLBACK_TIMEOUT``: The query timed out before
460+
the results were processed
461+
* ``getdns.CALLBACK_ERROR``: An unspecified error
462+
occurred
463+
464+
The ``result`` argument contains a result object, with the
465+
query response
466+
467+
The ``userarg`` argument contains the optional user argument
468+
that was passed to the query at the time it was invoked.
469+
470+
The ``transaction_id`` argument contains the transaction_id
471+
associated with a particular query; this is the same
472+
transaction id that was returned when the query was invoked.
473+
474+
This is an example callback function:
475+
476+
.. code-block:: python
477+
478+
def cbk(type, result, userarg, tid):
479+
if type == getdns.CALLBACK_COMPLETE:
480+
status = result.status
481+
if status == getdns.GETDNS_RESPSTATUS_GOOD:
482+
for addr in result.just_address_answers:
483+
addr_type = addr['address_type']
484+
addr_data = addr['address_data']
485+
print '{0}: {1} {2}'.format(userarg, addr_type, addr_data)
486+
elif status == getdns.GETDNS_RESPSTATUS_NO_SECURE_ANSWERS:
487+
print "{0}: No DNSSEC secured responses found".format(hostname)
488+
else:
489+
print "{0}: getdns.address() returned error: {1}".format(hostname, status)
490+
elif type == getdns.CALLBACK_CANCEL:
491+
print 'Callback cancelled'
492+
elif type == getdns.CALLBACK_TIMEOUT:
493+
print 'Query timed out'
494+
else:
495+
print 'Unknown error'
496+
497+
498+
Utility methods
499+
---------------
500+
501+
At the present time we support one utility method.
502+
503+
.. py:method:: get_errorstr_by_id(id)
504+
505+
``getdns.get_errorstr_by_id`` returns a string containing
506+
text describing a getdns return code, helping to make
507+
reporting errors to users a little easier. For example:
508+
509+
.. code-block:: python
510+
511+
if results.replies_full['status'] != getdns.GETDNS_RESPSTATUS_GOOD:
512+
print(getdns.get_errorstr_by_id(id=results.replies_full['status'])
513+
sys.exit(1)
514+
515+

doc/index.rst

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ getdns: Python bindings for getdns
1010
for the `getdns <http://getdnsapi.net/>`_ API. getdns is a
1111
modern, asynchronous DNS API that simplifies access to
1212
advanced DNS features, including DNSSEC. The API
13-
`specification <http://www.vpnc.org/getdns-api/>`_ was
13+
`specification <http://getdnsapi.net/spec/>`_ was
1414
developed by Paul Hoffman. getdns is built on top of the
1515
getdns implementation developed as a joint project between
1616
`Verisign Labs
@@ -29,7 +29,7 @@ This version of getdns has been built and tested against Python
2929
2.7. We also expect these other prerequisites to be
3030
installed:
3131

32-
* `libgetdns <http://getdnsapi.net/>`_, version 0.1.2 or later
32+
* `libgetdns <http://getdnsapi.net/>`_, version 0.1.7 or later
3333
* `libldns <https://www.nlnetlabs.nl/projects/ldns/>`_,
3434
version 1.6.11 or later
3535
* `libunbound
@@ -45,7 +45,7 @@ as follows:
4545

4646
./configure --with-libevent
4747

48-
This release has been tested against libgetdns 0.1.5.
48+
This release has been tested against libgetdns 0.1.7.
4949

5050
Building
5151
========
@@ -86,7 +86,7 @@ examined directly, and the overall state of a given context can be
8686
queried with the Context.get_api_information() method.
8787

8888
See section 8 of the `API
89-
specification <http://www.vpnc.org/getdns-api/>`_
89+
specification <http://getdnsapi.net/spec/>`_
9090

9191

9292
Examples
@@ -105,20 +105,21 @@ results to the screen:
105105
sys.exit(1)
106106
107107
ctx = getdns.Context()
108-
extensions = { "return_both_v4_and_v6" : getdns.GETDNS_EXTENSION_TRUE }
109-
results = ctx.address(name=sys.argv[1], extensions=extensions)
110-
if results["status"] == getdns.GETDNS_RESPSTATUS_GOOD:
108+
extensions = { "return_both_v4_and_v6" :
109+
getdns.GETDNS_EXTENSION_TRUE }
110+
results = ctx.address(name=sys.argv[1],
111+
extensions=extensions)
112+
if results.status == getdns.RESPSTATUS_GOOD:
111113
sys.stdout.write("Addresses: ")
112-
113-
for addr in results["just_address_answers"]:
114+
115+
for addr in results.just_address_answers:
114116
print " {0}".format(addr["address_data"])
115117
sys.stdout.write("\n\n")
116118
print "Entire results tree: "
117-
pprint.pprint(results)
118-
if results["status"] == getdns.GETDNS_RESPSTATUS_NO_NAME:
119+
pprint.pprint(results.replies_tree)
120+
if results.status == getdns.RESPSTATUS_NO_NAME:
119121
print "{0} not found".format(sys.argv[1])
120122
121-
122123
if __name__ == "__main__":
123124
main()
124125
@@ -149,27 +150,31 @@ In this example, we do a DNSSEC query and check the response:
149150
sys.exit(1)
150151
151152
ctx = getdns.Context()
152-
extensions = { "return_both_v4_and_v6" : getdns.GETDNS_EXTENSION_TRUE,
153-
"dnssec_return_status" : getdns.GETDNS_EXTENSION_TRUE }
154-
results = ctx.address(name=sys.argv[1], extensions=extensions)
155-
if results["status"] == getdns.GETDNS_RESPSTATUS_GOOD:
153+
extensions = { "return_both_v4_and_v6" :
154+
getdns.EXTENSION_TRUE,
155+
"dnssec_return_status" :
156+
getdns.EXTENSION_TRUE }
157+
results = ctx.address(name=sys.argv[1],
158+
extensions=extensions)
159+
if results.status == getdns.RESPSTATUS_GOOD:
156160
sys.stdout.write("Addresses: ")
157-
for addr in results["just_address_answers"]:
161+
for addr in results.just_address_answers:
158162
print " {0}".format(addr["address_data"])
159163
sys.stdout.write("\n")
160164
161-
for result in results["replies_tree"]:
165+
for result in results.replies_tree:
162166
if "dnssec_status" in result.keys():
163-
print "{0}: dnssec_status: {1}".format(result["canonical_name"],
167+
print "{0}: dnssec_status:
168+
{1}".format(result["canonical_name"],
164169
dnssec_message(result["dnssec_status"]))
165170
166-
if results["status"] == getdns.GETDNS_RESPSTATUS_NO_NAME:
171+
if results.status == getdns.RESPSTATUS_NO_NAME:
167172
print "{0} not found".format(sys.argv[1])
168173
169174
170175
if __name__ == "__main__":
171176
main()
172-
177+
173178
174179
Known issues
175180
============
@@ -186,7 +191,7 @@ Contents:
186191

187192
functions
188193
response
189-
194+
exceptions
190195

191196

192197
Indices and tables

0 commit comments

Comments
 (0)