BIND 10 trac1202, updated. 4a605525cda67bea8c43ca8b3eae6e6749797450 Merge branch 'master' into trac1202

BIND 10 source code commits bind10-changes at lists.isc.org
Thu Sep 29 12:48:00 UTC 2011


The branch, trac1202 has been updated
       via  4a605525cda67bea8c43ca8b3eae6e6749797450 (commit)
       via  acb299784ddbf280aac6ee5a78977c9acbf1fd32 (commit)
       via  250ce2abb3d6b48fce778b5e0c651d57582aff7c (commit)
       via  99be45a44f97942f9327b16aff368f1650994e0e (commit)
       via  7592596f7a9f8dce2e5e8d9311cc40c5199c66e3 (commit)
       via  c24c42a5e29444313efee6528f172ad66452050d (commit)
       via  05eaa177051b212669c2a7b9e2194c3e9ba47f14 (commit)
       via  9797d47ab90761c50020f78d5a55fb2672ffd7c0 (commit)
       via  000164d51a974acf3846a6b0a7795f484e915161 (commit)
       via  7dfa14ccdb6777ccacb99fe0d716b7d63654426f (commit)
       via  38816f95cc01f1c7aeec1d42bde3febb308dd98f (commit)
       via  0f8868d1ed7d479d05e2a70de67897d133d41ef9 (commit)
       via  bc03b37015ab6ea23cbec70dbd299c74fb001aba (commit)
       via  e56e0f7d1ad206f1ebc26e285d82a8e7ff6390e1 (commit)
       via  7d2b0148161460b928cf39c7c2969d95d2870d9c (commit)
       via  688867daa34ade5075443c77535f80e1d2d76743 (commit)
       via  d36ded7d95a695f0412f6ccdb59bf55fc600e9d3 (commit)
       via  b8e90124c19177e0b6b33bd624e244860e2424b3 (commit)
       via  5cf1b7ab58c42675c1396fbbd5b1aaf037eb8d19 (commit)
       via  17d9827aa40e363650d1698fddba9204f27b5171 (commit)
       via  27f447c8b054b17d96abfba431568c1ffe017f0a (commit)
       via  70bba1b3f811261fcef30694568245e83cd64bc5 (commit)
       via  6c5f8867a45f40411594372bca09c04ddf5c0002 (commit)
       via  f1fef139dbc592aa4c7071d47e38e14487ab72e7 (commit)
       via  2c8b76ed408547789f2e26ad76773e40e316a392 (commit)
       via  eefa62a767ec09c20d679876842e15e9d3742499 (commit)
       via  58845974d57ee0cd0b261b00d1ededccc7bde105 (commit)
       via  d49e3c5e79e00b59e518c4bc1f71882adf721696 (commit)
       via  06a24c688282b61dd2ce5b6c00608bee34ae3563 (commit)
       via  b902e70583a9dfb1ee410e297e2da4c8b944ba8d (commit)
       via  09349cf206ee9e68618713b97e621b7ef2a6c0a9 (commit)
       via  ff1bd2a00278bc753a7d035fd5020ff936df1882 (commit)
       via  c89f3a2f43fd7fe70bcb199fad0ccf94364b1ebe (commit)
       via  4c86025464db4603ec07490169aaf4b77868057b (commit)
       via  842fc917163f0b8cb2a703a4c7fe078d944932e8 (commit)
       via  0eb576518f81c3758c7dbaa2522bd8302b1836b3 (commit)
       via  68cf1ccf20ecfcc1e06de69fcd50d13cf8b5e1e0 (commit)
       via  bd0c874dda60a0f5e235b653e1bb63716cb385f8 (commit)
       via  b6709a7001e4812c4ed774ef0ff3111fb654d199 (commit)
       via  9b4326dc093b71bcd77a527111ea6778795bf068 (commit)
       via  2c5b2fc19c21dd12747eb960baee65759847a118 (commit)
       via  0aa89cf84c78a9ee8b97a51c17b3982324021f81 (commit)
       via  d9dd4c5a7438c152f6c9ae2bcc4c9f5ee598728b (commit)
       via  03da93322b956e003882c09a8d4ea949f790dbc4 (commit)
       via  bfa93c0ee79935bf37d379065e219ba0afb0c4e3 (commit)
       via  7a061c2e82d62e2b275cb5a8d7460dce7d36f050 (commit)
       via  a6cbb14cc9c986d109983087313225829f1c91fe (commit)
       via  7cc32b7915532354ed7e2fd15f7ca5a9b9b64610 (commit)
       via  dd340b32df88083fdc17f682094b451f7dcdf6d6 (commit)
       via  30c277567f64d09c11cadcb173eef066efdaea07 (commit)
       via  ec2793914d1090db8c8d94a2f9b92ed97b1a6cba (commit)
       via  a59c7f28a458842b4edce2d6639639b17a85eb9f (commit)
       via  766db4a6100e34e6a29aa9c849b60ba80b551389 (commit)
       via  f7b5370a9bf82b0b480b75275349d8570ee83c4c (commit)
       via  12d62d54d33fbb1572a1aa3089b0d547d02924aa (commit)
       via  c38112d8b59bfb6e73b5fbc637fa9eaaae42c52d (commit)
       via  ccb4c0aa696918c579a0b80448fc93606152ec93 (commit)
       via  0fa8006ade38ac7206ff57934f3bb866be6407a2 (commit)
       via  b25df34f6a7582baff54dab59c4e033f6db4e42c (commit)
       via  715fee7daf2f966261d997e1b39888f14fb28a45 (commit)
       via  c3424869801ea8811106f8f97928ed5cd71efbff (commit)
       via  4e544fba3459913e23f86dc5e628665bd288c483 (commit)
       via  259955ba65c102bd36ec818ca4193aab311e983d (commit)
       via  1f81b4916fa3bd0cbf4f41cc7ad8f13450aa6481 (commit)
       via  6d6353cea42ed088df3c2c90c4c2741a1b8b2871 (commit)
       via  7efa61c40b94d3234dd7fc79a0fc7ae0f1b0a105 (commit)
       via  5c3a7ca7b3b28a7a163b0af3cbadc3d8fe7a702b (commit)
       via  54c6127e005c8e3dd82cd97d49aca23f5a5d8029 (commit)
       via  b6261f09b53af42a26d88fd50d74ab1e84524cce (commit)
       via  8634aa9cab1c2205629540b4d99b88847148bd80 (commit)
       via  d1a1871cc6c93ababba62f42bcab5205320b8867 (commit)
       via  2a5c5383e3df0e625367bf85b740f62bf777b211 (commit)
       via  af10f1ef696ee94f817bc389e0e8b6cd08234333 (commit)
       via  f16de89251e4607eb413df666a64022c50478a4c (commit)
       via  3eb0dedb8a5d9835b394484c6112a4b2fcbe9d51 (commit)
       via  2f8c4b3da6060a9b57e944726dd61cb1b2a19906 (commit)
       via  4e93ba217318854742144bf0b8e30f4c3614db92 (commit)
       via  ee468e8f02f1cd1bcf09da75170ed62dc230b70e (commit)
       via  433f29fd44d8dd6c940e49ee2657b769d70781fe (commit)
       via  f0274b7451761b2dc48c0be148ecd8563c9800da (commit)
       via  45ef63790b34ebc2d26081609bb168aefee800dc (commit)
       via  38d80ef7186ac2b18ed234a825894f5f78fc90b1 (commit)
       via  88bee2515653d3b5481608bc92a1956c7ea7cf48 (commit)
       via  e9286ce511be095f2b16b1b7bc503b1e4377593d (commit)
       via  723a6d1f333f1d513d5e4fe26a8ee7611767c9fc (commit)
       via  88fe1bafce118f40d256097c2bfbdf9e53553784 (commit)
       via  cbf08d56345922d754182b941b84b18bfddabcda (commit)
       via  84a95705e1e8219187e75433baec2fd2fc8ba2fe (commit)
       via  aa5fd84d438cf165c9836fa545d15c33781401af (commit)
       via  fac67afceead36ba7296e194942811d9ed3b437b (commit)
       via  90b740caf4cc5d207dfa2ac98f1c73d9818792e2 (commit)
       via  0ea828cb5c74b0f9a254aeab2c7d31ff214371e5 (commit)
       via  170a0661dfb17014a62cd2eeaaa99e408bc55a14 (commit)
       via  b12f4e55007ee2e8130991f322e782bb31a8a289 (commit)
       via  18083458382473b414a3fc7f57623d2241f487ef (commit)
       via  fbe4ee1f76237fdd586638ce1ded4c6e5bd0bf1d (commit)
       via  9c53309978b4a4bf684b3abbb853876c5413f875 (commit)
       via  8ee5844e8dc3ec7d99a5890bdc85f54afd8886b6 (commit)
       via  c9ad781ebbaebb2e57956ac9eda542eaa88a743b (commit)
       via  9f441d72a245e3ccce2ee014adaa0ad62e7b0d29 (commit)
       via  51c4b53945599a72d550d7380c7107e11b467d5c (commit)
       via  4d39f72b87677c194d282a9e93de67dc0adfb4f3 (commit)
       via  ece8bd155e646869b10fd08817ee7cd71c699c61 (commit)
       via  b59f898456b33294d71a333d3f3b4fe9dc81e3dd (commit)
       via  84d7ae48d44e055cb16e3900cf2c4b2262f6a6da (commit)
       via  3089b6fd6eff650dc06c0698b80eae1595986677 (commit)
       via  3a9dc4fbd7dab867829ba3299d86c2f5b58d864f (commit)
       via  5859f177250685fbd49c9562ffc3e984b9d5ebae (commit)
       via  4948e0c8965c3d39b6e1bcb1bdb12b9615260a27 (commit)
       via  03e9f45f8a6584a373f1bd15f01f56d9296c842a (commit)
       via  cb4d8443645a5c3e973b4e2477198686d8d8c507 (commit)
       via  f847a5e079ceae0346b84fb320ed06ce9b443a63 (commit)
       via  05512e090c6c3cb852cebdb85ae7c12e8001603b (commit)
       via  c35f6b15bb6b703154e05399266dd2051ef9cfa9 (commit)
       via  3f2864bf1271ca525858cf3e1fa641e3496eec59 (commit)
       via  f8720ba467d8e107c512160a5502caf9be58a425 (commit)
       via  38af8a4225e8c82564758e8a5629da438220bc87 (commit)
       via  c5e0db2b7d8fbdb13548e01310f623f131ea0e9c (commit)
       via  26c7bfe851f00422beb442a77d25cc0887557b79 (commit)
       via  f5239632a06383f2b4f6825cb6a006ceb8bea417 (commit)
       via  680f05c35753bf1f70392d25b1e6310cf46476ce (commit)
       via  b12351c21ee92a13536aa89331cc73bd166dbe5f (commit)
       via  2e1dceedf6a4f661a8d7e57757b28f9f6cb1a9b3 (commit)
       via  df69ad0d0231218610f68ecb2b1953ae7f28fa68 (commit)
       via  5b713ea8e5fd35fdb1ab7ff953e010ef9b60f98c (commit)
       via  88095bed9cbc3e39c61eb0ea7dee1646ff13ac7e (commit)
       via  b557ab47f3355f5fc7d4f87dfa9e4a15e7e9f3e3 (commit)
       via  4e12574323ca3db3e985acee0540c603b2b33124 (commit)
       via  3fc53ba91b92ad40ebbf46272f57a45e3d2e3a27 (commit)
       via  5157876271e945703ee699f07442ee1a72bba362 (commit)
      from  f0ff0a2f69bcfae3e2a30a3bdeae37b475ae9106 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                                          |   13 +
 configure.ac                                       |   15 +
 src/bin/auth/query.cc                              |    7 +
 src/bin/auth/tests/query_unittest.cc               |    4 +
 src/bin/bind10/Makefile.am                         |   17 +-
 src/bin/bind10/bind10_src.py.in                    |    2 +-
 src/bin/bind10/run_bind10.sh.in                    |    4 +-
 src/bin/bind10/tests/Makefile.am                   |    4 +-
 src/bin/bindctl/Makefile.am                        |    2 +
 src/bin/bindctl/run_bindctl.sh.in                  |    4 +-
 src/bin/bindctl/tests/Makefile.am                  |    4 +-
 src/bin/cfgmgr/b10-cfgmgr.py.in                    |    2 +-
 src/bin/cfgmgr/plugins/tests/Makefile.am           |    6 +-
 src/bin/cfgmgr/tests/Makefile.am                   |    6 +-
 src/bin/cmdctl/Makefile.am                         |   15 +-
 src/bin/cmdctl/cmdctl.py.in                        |    2 +-
 src/bin/cmdctl/run_b10-cmdctl.sh.in                |   10 +-
 src/bin/cmdctl/tests/Makefile.am                   |    4 +-
 src/bin/dhcp6/tests/Makefile.am                    |    4 +-
 src/bin/loadzone/Makefile.am                       |    1 +
 src/bin/loadzone/run_loadzone.sh.in                |    4 +-
 src/bin/loadzone/tests/correct/Makefile.am         |    4 +-
 src/bin/loadzone/tests/correct/correct_test.sh.in  |    2 +-
 src/bin/loadzone/tests/error/Makefile.am           |    4 +-
 src/bin/loadzone/tests/error/error_test.sh.in      |    2 +-
 src/bin/msgq/tests/Makefile.am                     |    4 +-
 src/bin/stats/Makefile.am                          |   25 +-
 src/bin/stats/stats.py.in                          |    2 +-
 src/bin/stats/stats_httpd.py.in                    |    2 +-
 src/bin/stats/tests/Makefile.am                    |    4 +-
 src/bin/stats/tests/isc/Makefile.am                |    2 +-
 src/bin/stats/tests/isc/log_messages/Makefile.am   |    7 +
 src/bin/stats/tests/isc/log_messages/__init__.py   |   18 +
 .../tests/isc/log_messages/stats_httpd_messages.py |   16 +
 .../stats/tests/isc/log_messages/stats_messages.py |   16 +
 src/bin/tests/Makefile.am                          |    4 +-
 src/bin/xfrin/Makefile.am                          |   15 +-
 src/bin/xfrin/tests/Makefile.am                    |    4 +-
 src/bin/xfrin/xfrin.py.in                          |    2 +-
 src/bin/xfrout/Makefile.am                         |   15 +-
 src/bin/xfrout/tests/Makefile.am                   |    4 +-
 src/bin/xfrout/xfrout.py.in                        |    2 +-
 src/bin/zonemgr/Makefile.am                        |   14 +-
 src/bin/zonemgr/tests/Makefile.am                  |    4 +-
 src/bin/zonemgr/zonemgr.py.in                      |    2 +-
 src/lib/Makefile.am                                |    6 +-
 src/lib/datasrc/Makefile.am                        |    1 +
 src/lib/datasrc/database.cc                        |  469 +++++++++----
 src/lib/datasrc/database.h                         |  126 +++--
 src/lib/datasrc/datasrc_messages.mes               |    5 +
 src/lib/datasrc/memory_datasrc.cc                  |    6 +
 src/lib/datasrc/memory_datasrc.h                   |    6 +
 src/lib/datasrc/sqlite3_accessor.cc                |  105 +++-
 src/lib/datasrc/sqlite3_accessor.h                 |    4 +
 src/lib/datasrc/tests/database_unittest.cc         |  279 ++++++++-
 src/lib/datasrc/tests/memory_datasrc_unittest.cc   |    8 +
 src/lib/datasrc/tests/sqlite3_accessor_unittest.cc |   39 ++
 src/lib/datasrc/zone.h                             |   73 ++-
 src/lib/dns/Makefile.am                            |    3 +
 src/lib/dns/character_string.cc                    |  140 ++++
 src/lib/dns/character_string.h                     |   57 ++
 src/lib/dns/python/Makefile.am                     |   52 +-
 src/lib/dns/python/edns_python.cc                  |  262 ++++----
 src/lib/dns/python/edns_python.h                   |   64 ++
 src/lib/dns/python/message_python.cc               |  465 ++++++--------
 src/lib/dns/python/message_python.h                |   40 ++
 src/lib/dns/python/messagerenderer_python.cc       |   94 ++--
 src/lib/dns/python/messagerenderer_python.h        |   37 +-
 src/lib/dns/python/name_python.cc                  |  133 +---
 src/lib/dns/python/name_python.h                   |   45 +-
 src/lib/dns/python/opcode_python.cc                |  231 +++----
 src/lib/dns/python/opcode_python.h                 |   64 ++
 src/lib/dns/python/pydnspp.cc                      |  689 +++++++++++++++++++-
 src/lib/dns/python/pydnspp_common.cc               |   36 +
 src/lib/dns/python/pydnspp_common.h                |    2 -
 src/lib/dns/python/pydnspp_towire.h                |    4 +-
 src/lib/dns/python/question_python.cc              |  271 +++++----
 src/lib/dns/python/question_python.h               |   66 ++
 src/lib/dns/python/rcode_python.cc                 |  109 ++--
 src/lib/dns/python/rcode_python.h                  |   49 +-
 src/lib/dns/python/rdata_python.cc                 |  289 +++++----
 src/lib/dns/python/rdata_python.h                  |   68 ++
 src/lib/dns/python/rrclass_python.cc               |  303 +++++----
 src/lib/dns/python/rrclass_python.h                |   68 ++
 src/lib/dns/python/rrset_python.cc                 |  494 ++++++++------
 src/lib/dns/python/rrset_python.h                  |   78 +++
 src/lib/dns/python/rrttl_python.cc                 |  281 ++++----
 src/lib/dns/python/rrttl_python.h                  |   67 ++
 src/lib/dns/python/rrtype_python.cc                |  348 +++++-----
 src/lib/dns/python/rrtype_python.h                 |   68 ++
 src/lib/dns/python/tests/Makefile.am               |    2 +-
 src/lib/dns/python/tsig_python.cc                  |  105 +--
 src/lib/dns/python/tsig_python.h                   |   28 +-
 src/lib/dns/python/tsig_rdata_python.cc            |   62 +-
 src/lib/dns/python/tsig_rdata_python.h             |   29 +-
 src/lib/dns/python/tsigerror_python.cc             |  105 +---
 src/lib/dns/python/tsigerror_python.h              |   10 +-
 src/lib/dns/python/tsigkey_python.cc               |  133 ++---
 src/lib/dns/python/tsigkey_python.h                |   52 +-
 src/lib/dns/python/tsigrecord_python.cc            |   82 +--
 src/lib/dns/python/tsigrecord_python.h             |   28 +-
 src/lib/dns/rdata/generic/hinfo_13.cc              |  129 ++++
 src/lib/dns/rdata/generic/hinfo_13.h               |   77 +++
 src/lib/dns/rdata/generic/naptr_35.cc              |  104 +---
 src/lib/dns/rdata/generic/naptr_35.h               |   20 +
 src/lib/dns/tests/Makefile.am                      |    2 +
 src/lib/dns/tests/character_string_unittest.cc     |   92 +++
 src/lib/dns/tests/rdata_hinfo_unittest.cc          |  115 ++++
 src/lib/python/isc/Makefile.am                     |    3 +-
 src/lib/python/isc/__init__.py                     |    7 +-
 src/lib/python/isc/acl/tests/Makefile.am           |    4 +-
 src/lib/python/isc/bind10/sockcreator.py           |    2 +-
 src/lib/python/isc/bind10/tests/Makefile.am        |    4 +-
 src/lib/python/isc/cc/tests/Makefile.am            |    4 +-
 src/lib/python/isc/config/Makefile.am              |   32 +-
 src/lib/python/isc/config/ccsession.py             |    2 +-
 src/lib/python/isc/config/cfgmgr.py                |    2 +-
 src/lib/python/isc/config/tests/Makefile.am        |    4 +-
 src/lib/python/isc/datasrc/Makefile.am             |   31 +-
 src/lib/python/isc/datasrc/__init__.py             |   12 +-
 src/lib/python/isc/datasrc/client_inc.cc           |  157 +++++
 src/lib/python/isc/datasrc/client_python.cc        |  264 ++++++++
 src/lib/python/isc/datasrc/client_python.h         |   35 +
 src/lib/python/isc/datasrc/datasrc.cc              |  225 +++++++
 src/lib/python/isc/datasrc/datasrc.h               |   50 ++
 src/lib/python/isc/datasrc/finder_inc.cc           |   96 +++
 src/lib/python/isc/datasrc/finder_python.cc        |  248 +++++++
 src/lib/python/isc/datasrc/finder_python.h         |   36 +
 src/lib/python/isc/datasrc/iterator_inc.cc         |   34 +
 src/lib/python/isc/datasrc/iterator_python.cc      |  202 ++++++
 src/lib/python/isc/datasrc/iterator_python.h       |   38 ++
 src/lib/python/isc/datasrc/tests/Makefile.am       |   10 +-
 src/lib/python/isc/datasrc/tests/datasrc_test.py   |  389 +++++++++++
 src/lib/python/isc/datasrc/updater_inc.cc          |  181 +++++
 src/lib/python/isc/datasrc/updater_python.cc       |  318 +++++++++
 src/lib/python/isc/datasrc/updater_python.h        |   39 ++
 src/lib/python/isc/dns/Makefile.am                 |    7 +
 src/lib/python/isc/log/tests/Makefile.am           |    8 +-
 src/lib/python/isc/log_messages/Makefile.am        |   30 +
 src/lib/python/isc/log_messages/README             |   68 ++
 src/lib/python/isc/log_messages/__init__.py        |    3 +
 src/lib/python/isc/log_messages/bind10_messages.py |    1 +
 src/lib/python/isc/log_messages/cfgmgr_messages.py |    1 +
 src/lib/python/isc/log_messages/cmdctl_messages.py |    1 +
 src/lib/python/isc/log_messages/config_messages.py |    1 +
 src/lib/python/isc/log_messages/gen-forwarder.sh   |   14 +
 .../python/isc/log_messages/notify_out_messages.py |    1 +
 .../isc/log_messages/stats_httpd_messages.py       |    1 +
 src/lib/python/isc/log_messages/stats_messages.py  |    1 +
 src/lib/python/isc/log_messages/work/Makefile.am   |   12 +
 .../python/isc/log_messages/work/__init__.py.in    |    3 +
 src/lib/python/isc/log_messages/xfrin_messages.py  |    1 +
 src/lib/python/isc/log_messages/xfrout_messages.py |    1 +
 .../python/isc/log_messages/zonemgr_messages.py    |    1 +
 src/lib/python/isc/net/tests/Makefile.am           |    4 +-
 src/lib/python/isc/notify/Makefile.am              |   17 +-
 src/lib/python/isc/notify/notify_out.py            |    2 +-
 src/lib/python/isc/notify/tests/Makefile.am        |    4 +-
 src/lib/python/isc/util/tests/Makefile.am          |    4 +-
 src/lib/util/python/pycppwrapper_util.h            |    2 +-
 src/lib/util/python/wrapper_template.cc            |    4 +-
 src/lib/util/python/wrapper_template.h             |    6 +-
 162 files changed, 7582 insertions(+), 2628 deletions(-)
 mode change 100644 => 100755 src/bin/loadzone/tests/correct/correct_test.sh.in
 mode change 100644 => 100755 src/bin/loadzone/tests/error/error_test.sh.in
 mode change 100644 => 100755 src/bin/stats/stats.py.in
 create mode 100644 src/bin/stats/tests/isc/log_messages/Makefile.am
 create mode 100644 src/bin/stats/tests/isc/log_messages/__init__.py
 create mode 100644 src/bin/stats/tests/isc/log_messages/stats_httpd_messages.py
 create mode 100644 src/bin/stats/tests/isc/log_messages/stats_messages.py
 create mode 100644 src/lib/dns/character_string.cc
 create mode 100644 src/lib/dns/character_string.h
 create mode 100644 src/lib/dns/python/edns_python.h
 create mode 100644 src/lib/dns/python/message_python.h
 create mode 100644 src/lib/dns/python/opcode_python.h
 create mode 100644 src/lib/dns/python/question_python.h
 create mode 100644 src/lib/dns/python/rdata_python.h
 create mode 100644 src/lib/dns/python/rrclass_python.h
 create mode 100644 src/lib/dns/python/rrset_python.h
 create mode 100644 src/lib/dns/python/rrttl_python.h
 create mode 100644 src/lib/dns/python/rrtype_python.h
 create mode 100644 src/lib/dns/rdata/generic/hinfo_13.cc
 create mode 100644 src/lib/dns/rdata/generic/hinfo_13.h
 create mode 100644 src/lib/dns/tests/character_string_unittest.cc
 create mode 100644 src/lib/dns/tests/rdata_hinfo_unittest.cc
 create mode 100644 src/lib/python/isc/datasrc/client_inc.cc
 create mode 100644 src/lib/python/isc/datasrc/client_python.cc
 create mode 100644 src/lib/python/isc/datasrc/client_python.h
 create mode 100644 src/lib/python/isc/datasrc/datasrc.cc
 create mode 100644 src/lib/python/isc/datasrc/datasrc.h
 create mode 100644 src/lib/python/isc/datasrc/finder_inc.cc
 create mode 100644 src/lib/python/isc/datasrc/finder_python.cc
 create mode 100644 src/lib/python/isc/datasrc/finder_python.h
 create mode 100644 src/lib/python/isc/datasrc/iterator_inc.cc
 create mode 100644 src/lib/python/isc/datasrc/iterator_python.cc
 create mode 100644 src/lib/python/isc/datasrc/iterator_python.h
 create mode 100644 src/lib/python/isc/datasrc/tests/datasrc_test.py
 create mode 100644 src/lib/python/isc/datasrc/updater_inc.cc
 create mode 100644 src/lib/python/isc/datasrc/updater_python.cc
 create mode 100644 src/lib/python/isc/datasrc/updater_python.h
 create mode 100644 src/lib/python/isc/dns/Makefile.am
 create mode 100644 src/lib/python/isc/log_messages/Makefile.am
 create mode 100644 src/lib/python/isc/log_messages/README
 create mode 100644 src/lib/python/isc/log_messages/__init__.py
 create mode 100644 src/lib/python/isc/log_messages/bind10_messages.py
 create mode 100644 src/lib/python/isc/log_messages/cfgmgr_messages.py
 create mode 100644 src/lib/python/isc/log_messages/cmdctl_messages.py
 create mode 100644 src/lib/python/isc/log_messages/config_messages.py
 create mode 100755 src/lib/python/isc/log_messages/gen-forwarder.sh
 create mode 100644 src/lib/python/isc/log_messages/notify_out_messages.py
 create mode 100644 src/lib/python/isc/log_messages/stats_httpd_messages.py
 create mode 100644 src/lib/python/isc/log_messages/stats_messages.py
 create mode 100644 src/lib/python/isc/log_messages/work/Makefile.am
 create mode 100644 src/lib/python/isc/log_messages/work/__init__.py.in
 create mode 100644 src/lib/python/isc/log_messages/xfrin_messages.py
 create mode 100644 src/lib/python/isc/log_messages/xfrout_messages.py
 create mode 100644 src/lib/python/isc/log_messages/zonemgr_messages.py

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index a71c8a8..d0565e1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+287.	[bug]*		jinmei
+	Python script files for log messages (xxx_messages.py) should have
+	been installed under the "isc" package.  This fix itself should
+	be a transparent change without affecting existing configurations
+	or other operational practices, but you may want to clean up the
+	python files from the common directly (such as "site-packages").
+	(Trac #1101, git 0eb576518f81c3758c7dbaa2522bd8302b1836b3)
+
+286.    [func]		ocean
+	libdns++: Implement the HINFO rrtype support according to RFC1034,
+	and RFC1035.
+	(Trac #1112, git 12d62d54d33fbb1572a1aa3089b0d547d02924aa)
+
 285.	[bug]		jelte
 	sqlite3 data source: fixed a race condition on initial startup,
 	when the database has not been initialized yet, and multiple
diff --git a/configure.ac b/configure.ac
index 6ae71bf..cd44b4e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -155,6 +155,16 @@ fi
 PYTHON_SITEPKG_DIR=${pyexecdir}
 AC_SUBST(PYTHON_SITEPKG_DIR)
 
+# This will be commonly used in various Makefile.am's that need to generate
+# python log messages.
+PYTHON_LOGMSGPKG_DIR="\$(top_builddir)/src/lib/python/isc/log_messages"
+AC_SUBST(PYTHON_LOGMSGPKG_DIR)
+
+# This is python package paths commonly used in python tests.  See
+# README of log_messages for why it's included.
+COMMON_PYTHON_PATH="\$(abs_top_builddir)/src/lib/python/isc/log_messages:\$(abs_top_srcdir)/src/lib/python:\$(abs_top_builddir)/src/lib/python"
+AC_SUBST(COMMON_PYTHON_PATH)
+
 # Check for python development environments
 if test -x ${PYTHON}-config; then
 	PYTHON_INCLUDES=`${PYTHON}-config --includes`
@@ -812,6 +822,7 @@ AC_CONFIG_FILES([Makefile
                  src/bin/stats/tests/isc/config/Makefile
                  src/bin/stats/tests/isc/util/Makefile
                  src/bin/stats/tests/isc/log/Makefile
+                 src/bin/stats/tests/isc/log_messages/Makefile
                  src/bin/stats/tests/testdata/Makefile
                  src/bin/stats/tests/http/Makefile
                  src/bin/usermgr/Makefile
@@ -834,12 +845,15 @@ AC_CONFIG_FILES([Makefile
                  src/lib/python/isc/util/tests/Makefile
                  src/lib/python/isc/datasrc/Makefile
                  src/lib/python/isc/datasrc/tests/Makefile
+                 src/lib/python/isc/dns/Makefile
                  src/lib/python/isc/cc/Makefile
                  src/lib/python/isc/cc/tests/Makefile
                  src/lib/python/isc/config/Makefile
                  src/lib/python/isc/config/tests/Makefile
                  src/lib/python/isc/log/Makefile
                  src/lib/python/isc/log/tests/Makefile
+                 src/lib/python/isc/log_messages/Makefile
+                 src/lib/python/isc/log_messages/work/Makefile
                  src/lib/python/isc/net/Makefile
                  src/lib/python/isc/net/tests/Makefile
                  src/lib/python/isc/notify/Makefile
@@ -938,6 +952,7 @@ AC_OUTPUT([doc/version.ent
            src/lib/python/isc/cc/tests/cc_test
            src/lib/python/isc/notify/tests/notify_out_test
            src/lib/python/isc/log/tests/log_console.py
+           src/lib/python/isc/log_messages/work/__init__.py
            src/lib/dns/gen-rdatacode.py
            src/lib/python/bind10_config.py
            src/lib/cc/session_config.h.pre
diff --git a/src/bin/auth/query.cc b/src/bin/auth/query.cc
index 898fff7..ab6404e 100644
--- a/src/bin/auth/query.cc
+++ b/src/bin/auth/query.cc
@@ -253,6 +253,13 @@ Query::process() const {
                 // Just empty answer with SOA in authority section
                 putSOA(*result.zone_finder);
                 break;
+            default:
+                // These are new result codes (WILDCARD and WILDCARD_NXRRSET)
+                // They should not happen from the in-memory and the database
+                // backend isn't used yet.
+                // TODO: Implement before letting the database backends in
+                isc_throw(isc::NotImplemented, "Unknown result code");
+                break;
         }
     }
 }
diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc
index 4b8f013..b2d1094 100644
--- a/src/bin/auth/tests/query_unittest.cc
+++ b/src/bin/auth/tests/query_unittest.cc
@@ -141,6 +141,10 @@ public:
     // Turn this on if you want it to return RRSIGs regardless of FIND_GLUE_OK
     void setIncludeRRSIGAnyway(bool on) { include_rrsig_anyway_ = on; }
 
+    Name findPreviousName(const Name&) const {
+        isc_throw(isc::NotImplemented, "Mock doesn't support previous name");
+    }
+
 private:
     typedef map<RRType, ConstRRsetPtr> RRsetStore;
     typedef map<Name, RRsetStore> Domains;
diff --git a/src/bin/bind10/Makefile.am b/src/bin/bind10/Makefile.am
index 6ab88d8..5ec0c9f 100644
--- a/src/bin/bind10/Makefile.am
+++ b/src/bin/bind10/Makefile.am
@@ -1,10 +1,16 @@
 SUBDIRS = . tests
 
 sbin_SCRIPTS = bind10
-CLEANFILES = bind10 bind10_src.pyc bind10_messages.py bind10_messages.pyc
+CLEANFILES = bind10 bind10_src.pyc
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/bind10_messages.py
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/bind10_messages.pyc
 
 pkglibexecdir = $(libexecdir)/@PACKAGE@
-pyexec_DATA = bind10_messages.py
+
+nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/bind10_messages.py
+pylogmessagedir = $(pyexecdir)/isc/log_messages/
+
+noinst_SCRIPTS = run_bind10.sh
 
 bind10dir = $(pkgdatadir)
 bind10_DATA = bob.spec
@@ -20,11 +26,12 @@ bind10.8: bind10.xml
 
 endif
 
-bind10_messages.py: bind10_messages.mes
-	$(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/bind10/bind10_messages.mes
+$(PYTHON_LOGMSGPKG_DIR)/work/bind10_messages.py : bind10_messages.mes
+	$(top_builddir)/src/lib/log/compiler/message \
+	-d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/bind10_messages.mes
 
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
-bind10: bind10_src.py
+bind10: bind10_src.py $(PYTHON_LOGMSGPKG_DIR)/work/bind10_messages.py
 	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
 	       -e "s|@@LIBEXECDIR@@|$(pkglibexecdir)|" bind10_src.py >$@
 	chmod a+x $@
diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in
index 81b24f1..28af8cc 100755
--- a/src/bin/bind10/bind10_src.py.in
+++ b/src/bin/bind10/bind10_src.py.in
@@ -66,7 +66,7 @@ import isc.cc
 import isc.util.process
 import isc.net.parse
 import isc.log
-from bind10_messages import *
+from isc.log_messages.bind10_messages import *
 import isc.bind10.sockcreator
 
 isc.log.init("b10-boss")
diff --git a/src/bin/bind10/run_bind10.sh.in b/src/bin/bind10/run_bind10.sh.in
index b5b9721..50e6e29 100755
--- a/src/bin/bind10/run_bind10.sh.in
+++ b/src/bin/bind10/run_bind10.sh.in
@@ -23,14 +23,14 @@ BIND10_PATH=@abs_top_builddir@/src/bin/bind10
 PATH=@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/resolver:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:@abs_top_builddir@/src/bin/dhcp6:@abs_top_builddir@/src/bin/sockcreator:$PATH
 export PATH
 
-PYTHONPATH=@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/python/isc/config:@abs_top_builddir@/src/lib/python/isc/acl/.libs:
+PYTHONPATH=@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/python/isc/config:@abs_top_builddir@/src/lib/python/isc/acl/.libs:@abs_top_builddir@/src/lib/python/isc/datasrc/.libs
 export PYTHONPATH
 
 # If necessary (rare cases), explicitly specify paths to dynamic libraries
 # required by loadable python modules.
 SET_ENV_LIBRARY_PATH=@SET_ENV_LIBRARY_PATH@
 if test $SET_ENV_LIBRARY_PATH = yes; then
-	@ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/cryptolink/.libs:@abs_top_builddir@/src/lib/cc/.libs:@abs_top_builddir@/src/lib/config/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/acl/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:$@ENV_LIBRARY_PATH@
+	@ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/cryptolink/.libs:@abs_top_builddir@/src/lib/cc/.libs:@abs_top_builddir@/src/lib/config/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/acl/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:@abs_top_builddir@/src/lib/datasrc/.libs:$@ENV_LIBRARY_PATH@
 	export @ENV_LIBRARY_PATH@
 fi
 
diff --git a/src/bin/bind10/tests/Makefile.am b/src/bin/bind10/tests/Makefile.am
index f388ba1..d54ee56 100644
--- a/src/bin/bind10/tests/Makefile.am
+++ b/src/bin/bind10/tests/Makefile.am
@@ -8,7 +8,7 @@ noinst_SCRIPTS = $(PYTESTS)
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
@@ -22,7 +22,7 @@ endif
 	echo Running test: $$pytest ; \
 	chmod +x $(abs_builddir)/$$pytest ; \
 	$(LIBRARY_PATH_PLACEHOLDER) \
-	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \
 	BIND10_MSGQ_SOCKET_FILE=$(abs_top_builddir)/msgq_socket \
 		$(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \
 	done
diff --git a/src/bin/bindctl/Makefile.am b/src/bin/bindctl/Makefile.am
index cd8bcb3..700f26e 100644
--- a/src/bin/bindctl/Makefile.am
+++ b/src/bin/bindctl/Makefile.am
@@ -5,6 +5,8 @@ man_MANS = bindctl.1
 
 EXTRA_DIST = $(man_MANS) bindctl.xml
 
+noinst_SCRIPTS = run_bindctl.sh
+
 python_PYTHON = __init__.py bindcmd.py cmdparse.py exception.py moduleinfo.py \
 		mycollections.py
 pythondir = $(pyexecdir)/bindctl
diff --git a/src/bin/bindctl/run_bindctl.sh.in b/src/bin/bindctl/run_bindctl.sh.in
index 8f6ba59..f4cc40c 100755
--- a/src/bin/bindctl/run_bindctl.sh.in
+++ b/src/bin/bindctl/run_bindctl.sh.in
@@ -20,14 +20,14 @@ export PYTHON_EXEC
 
 BINDCTL_PATH=@abs_top_builddir@/src/bin/bindctl
 
-PYTHONPATH=@abs_top_srcdir@/src/bin:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/bin:@abs_top_srcdir@/src/lib/python
+PYTHONPATH=@abs_top_srcdir@/src/bin:@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/bin:@abs_top_srcdir@/src/lib/python
 export PYTHONPATH
 
 # If necessary (rare cases), explicitly specify paths to dynamic libraries
 # required by loadable python modules.
 SET_ENV_LIBRARY_PATH=@SET_ENV_LIBRARY_PATH@
 if test $SET_ENV_LIBRARY_PATH = yes; then
-	@ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/cryptolink/.libs:@abs_top_builddir@/src/lib/cc/.libs:@abs_top_builddir@/src/lib/config/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:$@ENV_LIBRARY_PATH@
+	@ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/cryptolink/.libs:@abs_top_builddir@/src/lib/cc/.libs:@abs_top_builddir@/src/lib/config/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:@abs_top_builddir@/src/lib/datasrc/.libs:$@ENV_LIBRARY_PATH@
 	export @ENV_LIBRARY_PATH@
 fi
 
diff --git a/src/bin/bindctl/tests/Makefile.am b/src/bin/bindctl/tests/Makefile.am
index 891d413..3d08a17 100644
--- a/src/bin/bindctl/tests/Makefile.am
+++ b/src/bin/bindctl/tests/Makefile.am
@@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS)
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
@@ -19,6 +19,6 @@ endif
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	$(LIBRARY_PATH_PLACEHOLDER) \
-	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/bindctl:$(abs_top_srcdir)/src/bin  \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/bindctl:$(abs_top_srcdir)/src/bin  \
 	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
 	done
diff --git a/src/bin/cfgmgr/b10-cfgmgr.py.in b/src/bin/cfgmgr/b10-cfgmgr.py.in
index 8befbdf..2ccc430 100755
--- a/src/bin/cfgmgr/b10-cfgmgr.py.in
+++ b/src/bin/cfgmgr/b10-cfgmgr.py.in
@@ -28,7 +28,7 @@ import os.path
 import isc.log
 isc.log.init("b10-cfgmgr")
 from isc.config.cfgmgr import ConfigManager, ConfigManagerDataReadError, logger
-from cfgmgr_messages import *
+from isc.log_messages.cfgmgr_messages import *
 
 isc.util.process.rename()
 
diff --git a/src/bin/cfgmgr/plugins/tests/Makefile.am b/src/bin/cfgmgr/plugins/tests/Makefile.am
index 07b7a85..ffea2d7 100644
--- a/src/bin/cfgmgr/plugins/tests/Makefile.am
+++ b/src/bin/cfgmgr/plugins/tests/Makefile.am
@@ -7,7 +7,7 @@ EXTRA_DIST = $(PYTESTS)
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
@@ -19,8 +19,8 @@ if ENABLE_PYTHON_COVERAGE
 endif
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
-	env B10_TEST_PLUGIN_DIR=$(abs_srcdir)/..:$(abs_builddir)/.. \
-	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/cfgmgr:$(abs_top_builddir)/src/lib/dns/python/.libs \
+	B10_TEST_PLUGIN_DIR=$(abs_srcdir)/..:$(abs_builddir)/.. \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/cfgmgr:$(abs_top_builddir)/src/lib/dns/python/.libs \
 	$(LIBRARY_PATH_PLACEHOLDER) \
 	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
 	done
diff --git a/src/bin/cfgmgr/tests/Makefile.am b/src/bin/cfgmgr/tests/Makefile.am
index 99f8cc9..a2e43ff 100644
--- a/src/bin/cfgmgr/tests/Makefile.am
+++ b/src/bin/cfgmgr/tests/Makefile.am
@@ -8,7 +8,7 @@ EXTRA_DIST = testdata/plugins/testplugin.py
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
@@ -21,9 +21,9 @@ endif
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	chmod +x $(abs_builddir)/$$pytest ; \
-	env TESTDATA_PATH=$(abs_srcdir)/testdata \
+	TESTDATA_PATH=$(abs_srcdir)/testdata \
 	$(LIBRARY_PATH_PLACEHOLDER) \
-	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/cfgmgr:$(abs_top_builddir)/src/lib/python/isc/config \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/cfgmgr:$(abs_top_builddir)/src/lib/python/isc/config \
 	$(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \
 	done
 
diff --git a/src/bin/cmdctl/Makefile.am b/src/bin/cmdctl/Makefile.am
index fcd23f8..e302fa6 100644
--- a/src/bin/cmdctl/Makefile.am
+++ b/src/bin/cmdctl/Makefile.am
@@ -3,7 +3,9 @@ SUBDIRS = . tests
 pkglibexecdir = $(libexecdir)/@PACKAGE@
 
 pkglibexec_SCRIPTS = b10-cmdctl
-pyexec_DATA = cmdctl_messages.py
+
+nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/cmdctl_messages.py
+pylogmessagedir = $(pyexecdir)/isc/log_messages/
 
 b10_cmdctldir = $(pkgdatadir)
 
@@ -19,7 +21,9 @@ b10_cmdctl_DATA += cmdctl.spec
 
 EXTRA_DIST = $(CMDCTL_CONFIGURATIONS)
 
-CLEANFILES=	b10-cmdctl cmdctl.pyc cmdctl.spec cmdctl_messages.py cmdctl_messages.pyc
+CLEANFILES= b10-cmdctl cmdctl.pyc cmdctl.spec
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/cmdctl_messages.py
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/cmdctl_messages.pyc
 
 man_MANS = b10-cmdctl.8
 EXTRA_DIST += $(man_MANS) b10-cmdctl.xml cmdctl_messages.mes
@@ -34,11 +38,12 @@ endif
 cmdctl.spec: cmdctl.spec.pre
 	$(SED) -e "s|@@SYSCONFDIR@@|$(sysconfdir)|" cmdctl.spec.pre >$@
 
-cmdctl_messages.py: cmdctl_messages.mes
-	$(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/cmdctl/cmdctl_messages.mes
+$(PYTHON_LOGMSGPKG_DIR)/work/cmdctl_messages.py : cmdctl_messages.mes
+	$(top_builddir)/src/lib/log/compiler/message \
+	-d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/cmdctl_messages.mes
 
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
-b10-cmdctl: cmdctl.py cmdctl_messages.py
+b10-cmdctl: cmdctl.py $(PYTHON_LOGMSGPKG_DIR)/work/cmdctl_messages.py
 	$(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" cmdctl.py >$@
 	chmod a+x $@
 
diff --git a/src/bin/cmdctl/cmdctl.py.in b/src/bin/cmdctl/cmdctl.py.in
index 2f89894..fcd69b8 100755
--- a/src/bin/cmdctl/cmdctl.py.in
+++ b/src/bin/cmdctl/cmdctl.py.in
@@ -47,7 +47,7 @@ import isc.net.parse
 from optparse import OptionParser, OptionValueError
 from hashlib import sha1
 from isc.util import socketserver_mixin
-from cmdctl_messages import *
+from isc.log_messages.cmdctl_messages import *
 
 # TODO: these debug-levels are hard-coded here; we are planning on
 # creating a general set of debug levels, see ticket #1074. When done,
diff --git a/src/bin/cmdctl/run_b10-cmdctl.sh.in b/src/bin/cmdctl/run_b10-cmdctl.sh.in
index 6a519e1..7e63249 100644
--- a/src/bin/cmdctl/run_b10-cmdctl.sh.in
+++ b/src/bin/cmdctl/run_b10-cmdctl.sh.in
@@ -19,9 +19,17 @@ PYTHON_EXEC=${PYTHON_EXEC:- at PYTHON@}
 export PYTHON_EXEC
 
 CMD_CTRLD_PATH=@abs_top_builddir@/src/bin/cmdctl
-PYTHONPATH=@abs_top_srcdir@/src/lib/python
+PYTHONPATH=@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/python/isc/config:@abs_top_builddir@/src/lib/python/isc/acl/.libs:@abs_top_builddir@/src/lib/python/isc/datasrc/.libs
 export PYTHONPATH
 
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+SET_ENV_LIBRARY_PATH=@SET_ENV_LIBRARY_PATH@
+if test $SET_ENV_LIBRARY_PATH = yes; then
+        @ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/cryptolink/.libs:@abs_top_builddir@/src/lib/cc/.libs:@abs_top_builddir@/src/lib/config/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:@abs_top_builddir@/src/lib/datasrc/.libs:$@ENV_LIBRARY_PATH@
+        export @ENV_LIBRARY_PATH@
+fi
+
 BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
 export BIND10_MSGQ_SOCKET_FILE
 
diff --git a/src/bin/cmdctl/tests/Makefile.am b/src/bin/cmdctl/tests/Makefile.am
index e4ec9d4..89d89ea 100644
--- a/src/bin/cmdctl/tests/Makefile.am
+++ b/src/bin/cmdctl/tests/Makefile.am
@@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS)
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
@@ -19,7 +19,7 @@ endif
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	$(LIBRARY_PATH_PLACEHOLDER) \
-	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/cmdctl \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/cmdctl \
 	CMDCTL_SPEC_PATH=$(abs_top_builddir)/src/bin/cmdctl \
 	CMDCTL_SRC_PATH=$(abs_top_srcdir)/src/bin/cmdctl \
 	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am
index 4a0e918..231a3d9 100644
--- a/src/bin/dhcp6/tests/Makefile.am
+++ b/src/bin/dhcp6/tests/Makefile.am
@@ -8,14 +8,14 @@ EXTRA_DIST = $(PYTESTS)
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
 check-local:
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
-	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \
 	$(LIBRARY_PATH_PLACEHOLDER) \
 	BIND10_MSGQ_SOCKET_FILE=$(abs_top_builddir)/msgq_socket \
 		$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
diff --git a/src/bin/loadzone/Makefile.am b/src/bin/loadzone/Makefile.am
index 74d4dd4..a235d68 100644
--- a/src/bin/loadzone/Makefile.am
+++ b/src/bin/loadzone/Makefile.am
@@ -1,5 +1,6 @@
 SUBDIRS = . tests/correct tests/error
 bin_SCRIPTS = b10-loadzone
+noinst_SCRIPTS = run_loadzone.sh
 
 CLEANFILES = b10-loadzone
 
diff --git a/src/bin/loadzone/run_loadzone.sh.in b/src/bin/loadzone/run_loadzone.sh.in
index 95de396..43b7920 100755
--- a/src/bin/loadzone/run_loadzone.sh.in
+++ b/src/bin/loadzone/run_loadzone.sh.in
@@ -18,14 +18,14 @@
 PYTHON_EXEC=${PYTHON_EXEC:- at PYTHON@}
 export PYTHON_EXEC
 
-PYTHONPATH=@abs_top_builddir@/src/lib/python
+PYTHONPATH=@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python
 export PYTHONPATH
 
 # If necessary (rare cases), explicitly specify paths to dynamic libraries
 # required by loadable python modules.
 SET_ENV_LIBRARY_PATH=@SET_ENV_LIBRARY_PATH@
 if test $SET_ENV_LIBRARY_PATH = yes; then
-	@ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/cryptolink/.libs:@abs_top_builddir@/src/lib/cc/.libs:@abs_top_builddir@/src/lib/config/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:$@ENV_LIBRARY_PATH@
+	@ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/cryptolink/.libs:@abs_top_builddir@/src/lib/cc/.libs:@abs_top_builddir@/src/lib/config/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:@abs_top_builddir@/src/lib/datasrc/.libs:$@ENV_LIBRARY_PATH@
 	export @ENV_LIBRARY_PATH@
 fi
 
diff --git a/src/bin/loadzone/tests/correct/Makefile.am b/src/bin/loadzone/tests/correct/Makefile.am
index 3507bfa..fb882ba 100644
--- a/src/bin/loadzone/tests/correct/Makefile.am
+++ b/src/bin/loadzone/tests/correct/Makefile.am
@@ -13,11 +13,13 @@ EXTRA_DIST += ttl2.db
 EXTRA_DIST += ttlext.db
 EXTRA_DIST += example.db
 
+noinst_SCRIPTS = correct_test.sh
+
 # If necessary (rare cases), explicitly specify paths to dynamic libraries
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # TODO: maybe use TESTS?
diff --git a/src/bin/loadzone/tests/correct/correct_test.sh.in b/src/bin/loadzone/tests/correct/correct_test.sh.in
old mode 100644
new mode 100755
index 509d8e5..d944451
--- a/src/bin/loadzone/tests/correct/correct_test.sh.in
+++ b/src/bin/loadzone/tests/correct/correct_test.sh.in
@@ -18,7 +18,7 @@
 PYTHON_EXEC=${PYTHON_EXEC:- at PYTHON@}
 export PYTHON_EXEC
 
-PYTHONPATH=@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/python
+PYTHONPATH=@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/python
 export PYTHONPATH
 
 LOADZONE_PATH=@abs_top_builddir@/src/bin/loadzone
diff --git a/src/bin/loadzone/tests/error/Makefile.am b/src/bin/loadzone/tests/error/Makefile.am
index 87bb1cf..03263b7 100644
--- a/src/bin/loadzone/tests/error/Makefile.am
+++ b/src/bin/loadzone/tests/error/Makefile.am
@@ -12,11 +12,13 @@ EXTRA_DIST += keyerror3.db
 EXTRA_DIST += originerr1.db
 EXTRA_DIST += originerr2.db
 
+noinst_SCRIPTS = error_test.sh
+
 # If necessary (rare cases), explicitly specify paths to dynamic libraries
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # TODO: use TESTS ?
diff --git a/src/bin/loadzone/tests/error/error_test.sh.in b/src/bin/loadzone/tests/error/error_test.sh.in
old mode 100644
new mode 100755
index d1d6bd1..94c5edb
--- a/src/bin/loadzone/tests/error/error_test.sh.in
+++ b/src/bin/loadzone/tests/error/error_test.sh.in
@@ -18,7 +18,7 @@
 PYTHON_EXEC=${PYTHON_EXEC:- at PYTHON@}
 export PYTHON_EXEC
 
-PYTHONPATH=@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/python
+PYTHONPATH=@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/python
 export PYTHONPATH
 
 LOADZONE_PATH=@abs_top_builddir@/src/bin/loadzone
diff --git a/src/bin/msgq/tests/Makefile.am b/src/bin/msgq/tests/Makefile.am
index 50c1e6e..50b218b 100644
--- a/src/bin/msgq/tests/Makefile.am
+++ b/src/bin/msgq/tests/Makefile.am
@@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS)
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
@@ -19,7 +19,7 @@ endif
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	$(LIBRARY_PATH_PLACEHOLDER) \
-	env PYTHONPATH=$(abs_top_builddir)/src/bin/msgq:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/msgq \
 	BIND10_TEST_SOCKET_FILE=$(builddir)/test_msgq_socket.sock \
 	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
 	done
diff --git a/src/bin/stats/Makefile.am b/src/bin/stats/Makefile.am
index e830f65..3289765 100644
--- a/src/bin/stats/Makefile.am
+++ b/src/bin/stats/Makefile.am
@@ -7,12 +7,17 @@ pkglibexec_SCRIPTS = b10-stats b10-stats-httpd
 b10_statsdir = $(pkgdatadir)
 b10_stats_DATA = stats.spec stats-httpd.spec stats-schema.spec
 b10_stats_DATA += stats-httpd-xml.tpl stats-httpd-xsd.tpl stats-httpd-xsl.tpl
-pyexec_DATA = stats_messages.py stats_httpd_messages.py
+
+nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/stats_messages.py
+nodist_pylogmessage_PYTHON += $(PYTHON_LOGMSGPKG_DIR)/work/stats_httpd_messages.py
+pylogmessagedir = $(pyexecdir)/isc/log_messages/
 
 CLEANFILES = b10-stats stats.pyc
 CLEANFILES += b10-stats-httpd stats_httpd.pyc
-CLEANFILES += stats_messages.py stats_messages.pyc
-CLEANFILES += stats_httpd_messages.py stats_httpd_messages.pyc
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/stats_messages.py
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/stats_messages.pyc
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/stats_httpd_messages.py
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/stats_httpd_messages.pyc
 
 man_MANS = b10-stats.8 b10-stats-httpd.8
 EXTRA_DIST = $(man_MANS) b10-stats.xml b10-stats-httpd.xml
@@ -30,18 +35,20 @@ b10-stats-httpd.8: b10-stats-httpd.xml
 
 endif
 
-stats_messages.py: stats_messages.mes
-	$(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/stats/stats_messages.mes
+$(PYTHON_LOGMSGPKG_DIR)/work/stats_messages.py : stats_messages.mes
+	$(top_builddir)/src/lib/log/compiler/message \
+	-d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/stats_messages.mes
 
-stats_httpd_messages.py: stats_httpd_messages.mes
-	$(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/stats/stats_httpd_messages.mes
+$(PYTHON_LOGMSGPKG_DIR)/work/stats_httpd_messages.py : stats_httpd_messages.mes
+	$(top_builddir)/src/lib/log/compiler/message \
+	-d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/stats_httpd_messages.mes
 
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
-b10-stats: stats.py
+b10-stats: stats.py $(PYTHON_LOGMSGPKG_DIR)/work/stats_messages.py
 	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|"  stats.py >$@
 	chmod a+x $@
 
-b10-stats-httpd: stats_httpd.py
+b10-stats-httpd: stats_httpd.py $(PYTHON_LOGMSGPKG_DIR)/work/stats_httpd_messages.py
 	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" stats_httpd.py >$@
 	chmod a+x $@
 
diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in
old mode 100644
new mode 100755
index 51d712b..afed544
--- a/src/bin/stats/stats.py.in
+++ b/src/bin/stats/stats.py.in
@@ -26,7 +26,7 @@ from isc.config.ccsession import ModuleCCSession, create_answer
 from isc.cc import Session, SessionError
 
 import isc.log
-from stats_messages import *
+from isc.log_messages.stats_messages import *
 
 isc.log.init("b10-stats")
 logger = isc.log.Logger("stats")
diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in
index 74298cf..6be6adf 100755
--- a/src/bin/stats/stats_httpd.py.in
+++ b/src/bin/stats/stats_httpd.py.in
@@ -35,7 +35,7 @@ import isc.config
 import isc.util.process
 
 import isc.log
-from stats_httpd_messages import *
+from isc.log_messages.stats_httpd_messages import *
 
 isc.log.init("b10-stats-httpd")
 logger = isc.log.Logger("stats-httpd")
diff --git a/src/bin/stats/tests/Makefile.am b/src/bin/stats/tests/Makefile.am
index dad6c48..ee79de2 100644
--- a/src/bin/stats/tests/Makefile.am
+++ b/src/bin/stats/tests/Makefile.am
@@ -8,7 +8,7 @@ CLEANFILES = fake_time.pyc fake_socket.pyc fake_select.pyc
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
@@ -21,7 +21,7 @@ endif
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	$(LIBRARY_PATH_PLACEHOLDER) \
-	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/stats:$(abs_top_builddir)/src/bin/stats/tests \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/stats:$(abs_top_builddir)/src/bin/stats/tests \
 	B10_FROM_SOURCE=$(abs_top_srcdir) \
 	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
 	done
diff --git a/src/bin/stats/tests/isc/Makefile.am b/src/bin/stats/tests/isc/Makefile.am
index d31395d..bdfa1eb 100644
--- a/src/bin/stats/tests/isc/Makefile.am
+++ b/src/bin/stats/tests/isc/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = cc config util log
+SUBDIRS = cc config util log log_messages
 EXTRA_DIST = __init__.py
 CLEANFILES = __init__.pyc
 
diff --git a/src/bin/stats/tests/isc/log_messages/Makefile.am b/src/bin/stats/tests/isc/log_messages/Makefile.am
new file mode 100644
index 0000000..90b4499
--- /dev/null
+++ b/src/bin/stats/tests/isc/log_messages/Makefile.am
@@ -0,0 +1,7 @@
+EXTRA_DIST = __init__.py stats_messages.py stats_httpd_messages.py
+CLEANFILES = __init__.pyc stats_messages.pyc stats_httpd_messages.pyc
+
+CLEANDIRS = __pycache__
+
+clean-local:
+	rm -rf $(CLEANDIRS)
diff --git a/src/bin/stats/tests/isc/log_messages/__init__.py b/src/bin/stats/tests/isc/log_messages/__init__.py
new file mode 100644
index 0000000..58e99e3
--- /dev/null
+++ b/src/bin/stats/tests/isc/log_messages/__init__.py
@@ -0,0 +1,18 @@
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+'''
+This is a fake package that acts as a forwarder to the real package.
+'''
diff --git a/src/bin/stats/tests/isc/log_messages/stats_httpd_messages.py b/src/bin/stats/tests/isc/log_messages/stats_httpd_messages.py
new file mode 100644
index 0000000..0adb0f0
--- /dev/null
+++ b/src/bin/stats/tests/isc/log_messages/stats_httpd_messages.py
@@ -0,0 +1,16 @@
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+from work.stats_httpd_messages import *
diff --git a/src/bin/stats/tests/isc/log_messages/stats_messages.py b/src/bin/stats/tests/isc/log_messages/stats_messages.py
new file mode 100644
index 0000000..c05a6a8
--- /dev/null
+++ b/src/bin/stats/tests/isc/log_messages/stats_messages.py
@@ -0,0 +1,16 @@
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+from work.stats_messages import *
diff --git a/src/bin/tests/Makefile.am b/src/bin/tests/Makefile.am
index 034152c..0ce992d 100644
--- a/src/bin/tests/Makefile.am
+++ b/src/bin/tests/Makefile.am
@@ -8,7 +8,7 @@ noinst_SCRIPTS = $(PYTESTS)
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
@@ -22,6 +22,6 @@ endif
 	echo Running test: $$pytest ; \
 	chmod +x $(abs_builddir)/$$pytest ; \
 	$(LIBRARY_PATH_PLACEHOLDER) \
-	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/dns/python/.libs \
 	$(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \
 	done
diff --git a/src/bin/xfrin/Makefile.am b/src/bin/xfrin/Makefile.am
index 0af9be6..8d80b22 100644
--- a/src/bin/xfrin/Makefile.am
+++ b/src/bin/xfrin/Makefile.am
@@ -6,9 +6,13 @@ pkglibexec_SCRIPTS = b10-xfrin
 
 b10_xfrindir = $(pkgdatadir)
 b10_xfrin_DATA = xfrin.spec
-pyexec_DATA = xfrin_messages.py
 
-CLEANFILES = b10-xfrin xfrin.pyc xfrinlog.py xfrin_messages.py xfrin_messages.pyc
+nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/xfrin_messages.py
+pylogmessagedir = $(pyexecdir)/isc/log_messages/
+
+CLEANFILES = b10-xfrin xfrin.pyc xfrinlog.py
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/xfrin_messages.py
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/xfrin_messages.pyc
 
 man_MANS = b10-xfrin.8
 EXTRA_DIST = $(man_MANS) b10-xfrin.xml
@@ -22,11 +26,12 @@ b10-xfrin.8: b10-xfrin.xml
 endif
 
 # Define rule to build logging source files from message file
-xfrin_messages.py: xfrin_messages.mes
-	$(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/xfrin/xfrin_messages.mes
+$(PYTHON_LOGMSGPKG_DIR)/work/xfrin_messages.py : xfrin_messages.mes
+	$(top_builddir)/src/lib/log/compiler/message \
+	-d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/xfrin_messages.mes
 
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
-b10-xfrin: xfrin.py xfrin_messages.py
+b10-xfrin: xfrin.py $(PYTHON_LOGMSGPKG_DIR)/work/xfrin_messages.py
 	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
 	       -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" xfrin.py >$@
 	chmod a+x $@
diff --git a/src/bin/xfrin/tests/Makefile.am b/src/bin/xfrin/tests/Makefile.am
index 0f485aa..3d56009 100644
--- a/src/bin/xfrin/tests/Makefile.am
+++ b/src/bin/xfrin/tests/Makefile.am
@@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS)
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/xfr/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
@@ -19,6 +19,6 @@ endif
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	$(LIBRARY_PATH_PLACEHOLDER) \
-	env PYTHONPATH=$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/bin/xfrin:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \
+	PYTHONPATH=$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/bin/xfrin:$(COMMON_PYTHON_PATH) \
 	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
 	done
diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in
index 07de8f0..8845b42 100755
--- a/src/bin/xfrin/xfrin.py.in
+++ b/src/bin/xfrin/xfrin.py.in
@@ -29,7 +29,7 @@ from isc.config.ccsession import *
 from isc.notify import notify_out
 import isc.util.process
 import isc.net.parse
-from xfrin_messages import *
+from isc.log_messages.xfrin_messages import *
 
 isc.log.init("b10-xfrin")
 logger = isc.log.Logger("xfrin")
diff --git a/src/bin/xfrout/Makefile.am b/src/bin/xfrout/Makefile.am
index c5492ad..6100e64 100644
--- a/src/bin/xfrout/Makefile.am
+++ b/src/bin/xfrout/Makefile.am
@@ -6,9 +6,13 @@ pkglibexec_SCRIPTS = b10-xfrout
 
 b10_xfroutdir = $(pkgdatadir)
 b10_xfrout_DATA = xfrout.spec
-pyexec_DATA = xfrout_messages.py
 
-CLEANFILES=	b10-xfrout xfrout.pyc xfrout.spec xfrout_messages.py xfrout_messages.pyc
+nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/xfrout_messages.py
+pylogmessagedir = $(pyexecdir)/isc/log_messages/
+
+CLEANFILES = b10-xfrout xfrout.pyc xfrout.spec
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/xfrout_messages.py
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/xfrout_messages.pyc
 
 man_MANS = b10-xfrout.8
 EXTRA_DIST = $(man_MANS) b10-xfrout.xml xfrout_messages.mes
@@ -21,14 +25,15 @@ b10-xfrout.8: b10-xfrout.xml
 endif
 
 # Define rule to build logging source files from message file
-xfrout_messages.py: xfrout_messages.mes
-	$(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/xfrout/xfrout_messages.mes
+$(PYTHON_LOGMSGPKG_DIR)/work/xfrout_messages.py : xfrout_messages.mes
+	$(top_builddir)/src/lib/log/compiler/message \
+	-d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/xfrout_messages.mes
 
 xfrout.spec: xfrout.spec.pre
 	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" xfrout.spec.pre >$@
 
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
-b10-xfrout: xfrout.py xfrout_messages.py
+b10-xfrout: xfrout.py $(PYTHON_LOGMSGPKG_DIR)/work/xfrout_messages.py
 	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
 	       -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" xfrout.py >$@
 	chmod a+x $@
diff --git a/src/bin/xfrout/tests/Makefile.am b/src/bin/xfrout/tests/Makefile.am
index 2f1e2ea..2e22e64 100644
--- a/src/bin/xfrout/tests/Makefile.am
+++ b/src/bin/xfrout/tests/Makefile.am
@@ -6,7 +6,7 @@ noinst_SCRIPTS = $(PYTESTS)
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/acl/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$(abs_top_builddir)/src/lib/acl/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
@@ -20,6 +20,6 @@ endif
 	echo Running test: $$pytest ; \
 	chmod +x $(abs_builddir)/$$pytest ; \
 	$(LIBRARY_PATH_PLACEHOLDER) \
-	env PYTHONPATH=$(abs_top_builddir)/src/bin/xfrout:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/util/io/.libs \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/xfrout:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/util/io/.libs \
 	$(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \
 	done
diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in
index fe42c54..144a1b8 100755
--- a/src/bin/xfrout/xfrout.py.in
+++ b/src/bin/xfrout/xfrout.py.in
@@ -35,7 +35,7 @@ import errno
 from optparse import OptionParser, OptionValueError
 from isc.util import socketserver_mixin
 
-from xfrout_messages import *
+from isc.log_messages.xfrout_messages import *
 
 isc.log.init("b10-xfrout")
 logger = isc.log.Logger("xfrout")
diff --git a/src/bin/zonemgr/Makefile.am b/src/bin/zonemgr/Makefile.am
index 34e6622..aa427fd 100644
--- a/src/bin/zonemgr/Makefile.am
+++ b/src/bin/zonemgr/Makefile.am
@@ -6,10 +6,13 @@ pkglibexec_SCRIPTS = b10-zonemgr
 
 b10_zonemgrdir = $(pkgdatadir)
 b10_zonemgr_DATA = zonemgr.spec
-pyexec_DATA = zonemgr_messages.py
+
+nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/zonemgr_messages.py
+pylogmessagedir = $(pyexecdir)/isc/log_messages/
 
 CLEANFILES  = b10-zonemgr zonemgr.pyc zonemgr.spec
-CLEANFILES += zonemgr_messages.py zonemgr_messages.pyc
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/zonemgr_messages.py
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/zonemgr_messages.pyc
 
 man_MANS = b10-zonemgr.8
 EXTRA_DIST = $(man_MANS) b10-zonemgr.xml zonemgr_messages.mes
@@ -22,13 +25,14 @@ b10-zonemgr.8: b10-zonemgr.xml
 endif
 
 # Build logging source file from message files
-zonemgr_messages.py: zonemgr_messages.mes
-	$(top_builddir)/src/lib/log/compiler/message -p $(top_srcdir)/src/bin/zonemgr/zonemgr_messages.mes
+$(PYTHON_LOGMSGPKG_DIR)/work/zonemgr_messages.py : zonemgr_messages.mes
+	$(top_builddir)/src/lib/log/compiler/message \
+	-d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/zonemgr_messages.mes
 
 zonemgr.spec: zonemgr.spec.pre
 	$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" zonemgr.spec.pre >$@
 
-b10-zonemgr: zonemgr.py
+b10-zonemgr: zonemgr.py $(PYTHON_LOGMSGPKG_DIR)/work/zonemgr_messages.py
 	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
 	       -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" zonemgr.py >$@
 	chmod a+x $@
diff --git a/src/bin/zonemgr/tests/Makefile.am b/src/bin/zonemgr/tests/Makefile.am
index 97f9b5e..769d332 100644
--- a/src/bin/zonemgr/tests/Makefile.am
+++ b/src/bin/zonemgr/tests/Makefile.am
@@ -7,7 +7,7 @@ CLEANFILES = initdb.file
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
@@ -20,6 +20,6 @@ endif
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	$(LIBRARY_PATH_PLACEHOLDER) \
-	env PYTHONPATH=$(abs_top_builddir)/src/bin/zonemgr:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/xfr/.libs \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/zonemgr:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/xfr/.libs \
 	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
 	done
diff --git a/src/bin/zonemgr/zonemgr.py.in b/src/bin/zonemgr/zonemgr.py.in
index d4de6a8..5c8d9b5 100755
--- a/src/bin/zonemgr/zonemgr.py.in
+++ b/src/bin/zonemgr/zonemgr.py.in
@@ -37,7 +37,7 @@ from isc.datasrc import sqlite3_ds
 from optparse import OptionParser, OptionValueError
 from isc.config.ccsession import *
 import isc.util.process
-from zonemgr_messages import *
+from isc.log_messages.zonemgr_messages import *
 
 # Initialize logging for called modules.
 isc.log.init("b10-zonemgr")
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 5adf150..04eee45 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -1,3 +1,3 @@
-SUBDIRS = exceptions util log cryptolink dns cc config acl python xfr \
-          bench asiolink asiodns nsas cache resolve testutils datasrc \
-          server_common
+SUBDIRS = exceptions util log cryptolink dns cc config acl xfr bench \
+          asiolink asiodns nsas cache resolve testutils datasrc \
+          server_common python
diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am
index 8f8a5ce..6b71388 100644
--- a/src/lib/datasrc/Makefile.am
+++ b/src/lib/datasrc/Makefile.am
@@ -30,6 +30,7 @@ libdatasrc_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
 libdatasrc_la_LIBADD += $(top_builddir)/src/lib/dns/libdns++.la
 libdatasrc_la_LIBADD += $(top_builddir)/src/lib/log/liblog.la
 libdatasrc_la_LIBADD += $(top_builddir)/src/lib/cc/libcc.la
+libdatasrc_la_LIBADD += $(SQLITE_LIBS)
 
 BUILT_SOURCES = datasrc_messages.h datasrc_messages.cc
 datasrc_messages.h datasrc_messages.cc: Makefile datasrc_messages.mes
diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc
index 2c5aaeb..e476297 100644
--- a/src/lib/datasrc/database.cc
+++ b/src/lib/datasrc/database.cc
@@ -174,105 +174,42 @@ private:
 };
 }
 
-std::pair<bool, isc::dns::RRsetPtr>
-DatabaseClient::Finder::getRRset(const isc::dns::Name& name,
-                                 const isc::dns::RRType* type,
-                                 bool want_cname, bool want_dname,
-                                 bool want_ns,
-                                 const isc::dns::Name* construct_name)
+DatabaseClient::Finder::FoundRRsets
+DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
+                                  bool check_ns, const string* construct_name)
 {
     RRsigStore sig_store;
     bool records_found = false;
-    isc::dns::RRsetPtr result_rrset;
+    std::map<RRType, RRsetPtr> result;
 
     // Request the context
     DatabaseAccessor::IteratorContextPtr
-        context(accessor_->getRecords(name.toText(), zone_id_));
+        context(accessor_->getRecords(name, zone_id_));
     // It must not return NULL, that's a bug of the implementation
     if (!context) {
-        isc_throw(isc::Unexpected, "Iterator context null at " +
-                  name.toText());
+        isc_throw(isc::Unexpected, "Iterator context null at " + name);
     }
 
     std::string columns[DatabaseAccessor::COLUMN_COUNT];
     if (construct_name == NULL) {
         construct_name = &name;
     }
+
+    const Name construct_name_object(*construct_name);
+
+    bool seen_cname(false);
+    bool seen_ds(false);
+    bool seen_other(false);
+    bool seen_ns(false);
+
     while (context->getNext(columns)) {
-        if (!records_found) {
-            records_found = true;
-        }
+        // The domain is not empty
+        records_found = true;
 
         try {
-            const isc::dns::RRType cur_type(columns[DatabaseAccessor::
-                                            TYPE_COLUMN]);
-            const isc::dns::RRTTL cur_ttl(columns[DatabaseAccessor::
-                                          TTL_COLUMN]);
-            // Ths sigtype column was an optimization for finding the
-            // relevant RRSIG RRs for a lookup. Currently this column is
-            // not used in this revised datasource implementation. We
-            // should either start using it again, or remove it from use
-            // completely (i.e. also remove it from the schema and the
-            // backend implementation).
-            // Note that because we don't use it now, we also won't notice
-            // it if the value is wrong (i.e. if the sigtype column
-            // contains an rrtype that is different from the actual value
-            // of the 'type covered' field in the RRSIG Rdata).
-            //cur_sigtype(columns[SIGTYPE_COLUMN]);
-
-            // Check for delegations before checking for the right type.
-            // This is needed to properly delegate request for the NS
-            // record itself.
-            //
-            // This happens with NS only, CNAME must be alone and DNAME
-            // is not checked in the exact queried domain.
-            if (want_ns && cur_type == isc::dns::RRType::NS()) {
-                if (result_rrset &&
-                    result_rrset->getType() != isc::dns::RRType::NS()) {
-                    isc_throw(DataSourceError, "NS found together with data"
-                              " in non-apex domain " + name.toText());
-                }
-                addOrCreate(result_rrset, *construct_name, getClass(),
-                            cur_type, cur_ttl,
-                            columns[DatabaseAccessor::RDATA_COLUMN],
-                            *accessor_);
-            } else if (type != NULL && cur_type == *type) {
-                if (result_rrset &&
-                    result_rrset->getType() == isc::dns::RRType::CNAME()) {
-                    isc_throw(DataSourceError, "CNAME found but it is not "
-                              "the only record for " + name.toText());
-                } else if (result_rrset && want_ns &&
-                           result_rrset->getType() == isc::dns::RRType::NS()) {
-                    isc_throw(DataSourceError, "NS found together with data"
-                              " in non-apex domain " + name.toText());
-                }
-                addOrCreate(result_rrset, *construct_name, getClass(),
-                            cur_type, cur_ttl,
-                            columns[DatabaseAccessor::RDATA_COLUMN],
-                            *accessor_);
-            } else if (want_cname && cur_type == isc::dns::RRType::CNAME()) {
-                // There should be no other data, so result_rrset should
-                // be empty.
-                if (result_rrset) {
-                    isc_throw(DataSourceError, "CNAME found but it is not "
-                              "the only record for " + name.toText());
-                }
-                addOrCreate(result_rrset, *construct_name, getClass(),
-                            cur_type, cur_ttl,
-                            columns[DatabaseAccessor::RDATA_COLUMN],
-                            *accessor_);
-            } else if (want_dname && cur_type == isc::dns::RRType::DNAME()) {
-                // There should be max one RR of DNAME present
-                if (result_rrset &&
-                    result_rrset->getType() == isc::dns::RRType::DNAME()) {
-                    isc_throw(DataSourceError, "DNAME with multiple RRs in " +
-                              name.toText());
-                }
-                addOrCreate(result_rrset, *construct_name, getClass(),
-                            cur_type, cur_ttl,
-                            columns[DatabaseAccessor::RDATA_COLUMN],
-                            *accessor_);
-            } else if (cur_type == isc::dns::RRType::RRSIG()) {
+            const RRType cur_type(columns[DatabaseAccessor::TYPE_COLUMN]);
+
+            if (cur_type == RRType::RRSIG()) {
                 // If we get signatures before we get the actual data, we
                 // can't know which ones to keep and which to drop...
                 // So we keep a separate store of any signature that may be
@@ -280,27 +217,76 @@ DatabaseClient::Finder::getRRset(const isc::dns::Name& name,
                 // done.
                 // A possible optimization here is to not store them for
                 // types we are certain we don't need
-                sig_store.addSig(isc::dns::rdata::createRdata(cur_type,
-                    getClass(), columns[DatabaseAccessor::RDATA_COLUMN]));
+                sig_store.addSig(rdata::createRdata(cur_type, getClass(),
+                     columns[DatabaseAccessor::RDATA_COLUMN]));
+            }
+
+            if (types.find(cur_type) != types.end()) {
+                // This type is requested, so put it into result
+                const RRTTL cur_ttl(columns[DatabaseAccessor::TTL_COLUMN]);
+                // Ths sigtype column was an optimization for finding the
+                // relevant RRSIG RRs for a lookup. Currently this column is
+                // not used in this revised datasource implementation. We
+                // should either start using it again, or remove it from use
+                // completely (i.e. also remove it from the schema and the
+                // backend implementation).
+                // Note that because we don't use it now, we also won't notice
+                // it if the value is wrong (i.e. if the sigtype column
+                // contains an rrtype that is different from the actual value
+                // of the 'type covered' field in the RRSIG Rdata).
+                //cur_sigtype(columns[SIGTYPE_COLUMN]);
+                addOrCreate(result[cur_type], construct_name_object,
+                            getClass(), cur_type, cur_ttl,
+                            columns[DatabaseAccessor::RDATA_COLUMN],
+                            *accessor_);
             }
-        } catch (const isc::dns::InvalidRRType& irt) {
+
+            if (cur_type == RRType::CNAME()) {
+                seen_cname = true;
+            } else if (cur_type == RRType::NS()) {
+                seen_ns = true;
+            } else if (cur_type == RRType::DS()) {
+                seen_ds = true;
+            } else if (cur_type != RRType::RRSIG() &&
+                       cur_type != RRType::NSEC3() &&
+                       cur_type != RRType::NSEC()) {
+                // NSEC and RRSIG can coexist with anything, otherwise
+                // we've seen something that can't live together with potential
+                // CNAME or NS
+                //
+                // NSEC3 lives in separate namespace from everything, therefore
+                // we just ignore it here for these checks as well.
+                seen_other = true;
+            }
+        } catch (const InvalidRRType&) {
             isc_throw(DataSourceError, "Invalid RRType in database for " <<
                       name << ": " << columns[DatabaseAccessor::
                       TYPE_COLUMN]);
-        } catch (const isc::dns::InvalidRRTTL& irttl) {
+        } catch (const InvalidRRTTL&) {
             isc_throw(DataSourceError, "Invalid TTL in database for " <<
                       name << ": " << columns[DatabaseAccessor::
                       TTL_COLUMN]);
-        } catch (const isc::dns::rdata::InvalidRdataText& ird) {
+        } catch (const rdata::InvalidRdataText&) {
             isc_throw(DataSourceError, "Invalid rdata in database for " <<
                       name << ": " << columns[DatabaseAccessor::
                       RDATA_COLUMN]);
         }
     }
-    if (result_rrset) {
-        sig_store.appendSignatures(result_rrset);
+    if (seen_cname && (seen_other || seen_ns || seen_ds)) {
+        isc_throw(DataSourceError, "CNAME shares domain " << name <<
+                  " with something else");
+    }
+    if (check_ns && seen_ns && seen_other) {
+        isc_throw(DataSourceError, "NS shares domain " << name <<
+                  " with something else");
     }
-    return (std::pair<bool, isc::dns::RRsetPtr>(records_found, result_rrset));
+    // Add signatures to all found RRsets
+    for (std::map<RRType, RRsetPtr>::iterator i(result.begin());
+         i != result.end(); ++ i) {
+        sig_store.appendSignatures(i->second);
+    }
+
+    return (FoundRRsets(records_found, result));
 }
 
 bool
@@ -317,6 +303,92 @@ DatabaseClient::Finder::hasSubdomains(const std::string& name) {
     return (context->getNext(columns));
 }
 
+// Some manipulation with RRType sets
+namespace {
+
+// Bunch of functions to construct specific sets of RRTypes we will
+// ask from it.
+typedef std::set<RRType> WantedTypes;
+
+const WantedTypes&
+NSEC_TYPES() {
+    static bool initialized(false);
+    static WantedTypes result;
+
+    if (!initialized) {
+        result.insert(RRType::NSEC());
+        initialized = true;
+    }
+    return (result);
+}
+
+const WantedTypes&
+DELEGATION_TYPES() {
+    static bool initialized(false);
+    static WantedTypes result;
+
+    if (!initialized) {
+        result.insert(RRType::DNAME());
+        result.insert(RRType::NS());
+        initialized = true;
+    }
+    return (result);
+}
+
+const WantedTypes&
+FINAL_TYPES() {
+    static bool initialized(false);
+    static WantedTypes result;
+
+    if (!initialized) {
+        result.insert(RRType::CNAME());
+        result.insert(RRType::NS());
+        result.insert(RRType::NSEC());
+        initialized = true;
+    }
+    return (result);
+}
+
+}
+
+RRsetPtr
+DatabaseClient::Finder::findNSECCover(const Name& name) {
+    try {
+        // Which one should contain the NSEC record?
+        const Name coverName(findPreviousName(name));
+        // Get the record and copy it out
+        const FoundRRsets found = getRRsets(coverName.toText(), NSEC_TYPES(),
+                                            coverName != getOrigin());
+        const FoundIterator
+            nci(found.second.find(RRType::NSEC()));
+        if (nci != found.second.end()) {
+            return (nci->second);
+        } else {
+            // The previous doesn't contain NSEC.
+            // Badly signed zone or a bug?
+
+            // FIXME: Currently, if the zone is not signed, we could get
+            // here. In that case we can't really throw, but for now, we can't
+            // recognize it. So we don't throw at all, enable it once
+            // we have a is_signed flag or something.
+#if 0
+            isc_throw(DataSourceError, "No NSEC in " +
+                      coverName.toText() + ", but it was "
+                      "returned as previous - "
+                      "accessor error? Badly signed zone?");
+#endif
+        }
+    }
+    catch (const isc::NotImplemented&) {
+        // Well, they want DNSSEC, but there is no available.
+        // So we don't provide anything.
+        LOG_INFO(logger, DATASRC_DATABASE_COVER_NSEC_UNSUPPORTED).
+            arg(accessor_->getDBName()).arg(name);
+    }
+    // We didn't find it, return nothing
+    return (RRsetPtr());
+}
+
 ZoneFinder::FindResult
 DatabaseClient::Finder::find(const isc::dns::Name& name,
                              const isc::dns::RRType& type,
@@ -326,10 +398,12 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
     // This variable is used to determine the difference between
     // NXDOMAIN and NXRRSET
     bool records_found = false;
-    bool glue_ok(options & FIND_GLUE_OK);
+    bool glue_ok((options & FIND_GLUE_OK) != 0);
+    const bool dnssec_data((options & FIND_DNSSEC) != 0);
+    bool get_cover(false);
     isc::dns::RRsetPtr result_rrset;
     ZoneFinder::Result result_status = SUCCESS;
-    std::pair<bool, isc::dns::RRsetPtr> found;
+    FoundRRsets found;
     logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS)
         .arg(accessor_->getDBName()).arg(name).arg(type);
     // In case we are in GLUE_OK mode and start matching wildcards,
@@ -337,11 +411,11 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
     isc::dns::RRsetPtr first_ns;
 
     // First, do we have any kind of delegation (NS/DNAME) here?
-    Name origin(getOrigin());
-    size_t origin_label_count(origin.getLabelCount());
+    const Name origin(getOrigin());
+    const size_t origin_label_count(origin.getLabelCount());
     // Number of labels in the last known non-empty domain
     size_t last_known(origin_label_count);
-    size_t current_label_count(name.getLabelCount());
+    const size_t current_label_count(name.getLabelCount());
     // This is how many labels we remove to get origin
     size_t remove_labels(current_label_count - origin_label_count);
 
@@ -349,35 +423,44 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
     for (int i(remove_labels); i > 0; --i) {
         Name superdomain(name.split(i));
         // Look if there's NS or DNAME (but ignore the NS in origin)
-        found = getRRset(superdomain, NULL, false, true,
-                         i != remove_labels && !glue_ok);
+        found = getRRsets(superdomain.toText(), DELEGATION_TYPES(),
+                          i != remove_labels);
         if (found.first) {
             // It contains some RRs, so it exists.
             last_known = superdomain.getLabelCount();
-            // In case we are in GLUE_OK, we want to store the highest
-            // encountered RRset.
-            if (glue_ok && !first_ns && i != remove_labels) {
-                first_ns = getRRset(superdomain, NULL, false, false,
-                                    true).second;
-            }
-        }
-        if (found.second) {
-            // We found something redirecting somewhere else
-            // (it can be only NS or DNAME here)
-            result_rrset = found.second;
-            if (result_rrset->getType() == isc::dns::RRType::NS()) {
+
+            const FoundIterator nsi(found.second.find(RRType::NS()));
+            const FoundIterator dni(found.second.find(RRType::DNAME()));
+            // In case we are in GLUE_OK mode, we want to store the
+            // highest encountered NS (but not apex)
+            if (glue_ok && !first_ns && i != remove_labels &&
+                nsi != found.second.end()) {
+                first_ns = nsi->second;
+            } else if (!glue_ok && i != remove_labels &&
+                       nsi != found.second.end()) {
+                // Do a NS delegation, but ignore NS in glue_ok mode. Ignore
+                // delegation in apex
                 LOG_DEBUG(logger, DBG_TRACE_DETAILED,
                           DATASRC_DATABASE_FOUND_DELEGATION).
                     arg(accessor_->getDBName()).arg(superdomain);
+                result_rrset = nsi->second;
                 result_status = DELEGATION;
-            } else {
+                // No need to go lower, found
+                break;
+            } else if (dni != found.second.end()) {
+                // Very similar with DNAME
                 LOG_DEBUG(logger, DBG_TRACE_DETAILED,
                           DATASRC_DATABASE_FOUND_DNAME).
                     arg(accessor_->getDBName()).arg(superdomain);
+                result_rrset = dni->second;
                 result_status = DNAME;
+                if (result_rrset->getRdataCount() != 1) {
+                    isc_throw(DataSourceError, "DNAME at " << superdomain <<
+                              " has " << result_rrset->getRdataCount() <<
+                              " rdata, 1 expected");
+                }
+                break;
             }
-            // Don't search more
-            break;
         }
     }
 
@@ -385,21 +468,37 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
         // Try getting the final result and extract it
         // It is special if there's a CNAME or NS, DNAME is ignored here
         // And we don't consider the NS in origin
-        found = getRRset(name, &type, true, false, name != origin && !glue_ok);
+
+        WantedTypes final_types(FINAL_TYPES());
+        final_types.insert(type);
+        found = getRRsets(name.toText(), final_types, name != origin);
         records_found = found.first;
-        result_rrset = found.second;
-        if (result_rrset && name != origin && !glue_ok &&
-            result_rrset->getType() == isc::dns::RRType::NS()) {
+
+        // NS records, CNAME record and Wanted Type records
+        const FoundIterator nsi(found.second.find(RRType::NS()));
+        const FoundIterator cni(found.second.find(RRType::CNAME()));
+        const FoundIterator wti(found.second.find(type));
+        if (name != origin && !glue_ok && nsi != found.second.end()) {
+            // There's a delegation at the exact node.
             LOG_DEBUG(logger, DBG_TRACE_DETAILED,
                       DATASRC_DATABASE_FOUND_DELEGATION_EXACT).
                 arg(accessor_->getDBName()).arg(name);
             result_status = DELEGATION;
-        } else if (result_rrset && type != isc::dns::RRType::CNAME() &&
-                   result_rrset->getType() == isc::dns::RRType::CNAME()) {
+            result_rrset = nsi->second;
+        } else if (type != isc::dns::RRType::CNAME() &&
+                   cni != found.second.end()) {
+            // A CNAME here
             result_status = CNAME;
-        }
-
-        if (!result_rrset && !records_found) {
+            result_rrset = cni->second;
+            if (result_rrset->getRdataCount() != 1) {
+                isc_throw(DataSourceError, "CNAME with " <<
+                          result_rrset->getRdataCount() <<
+                          " rdata at " << name << ", expected 1");
+            }
+        } else if (wti != found.second.end()) {
+            // Just get the answer
+            result_rrset = wti->second;
+        } else if (!records_found) {
             // Nothing lives here.
             // But check if something lives below this
             // domain and if so, pretend something is here as well.
@@ -408,23 +507,22 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
                           DATASRC_DATABASE_FOUND_EMPTY_NONTERMINAL).
                     arg(accessor_->getDBName()).arg(name);
                 records_found = true;
+                get_cover = dnssec_data;
             } else {
                 // It's not empty non-terminal. So check for wildcards.
                 // We remove labels one by one and look for the wildcard there.
                 // Go up to first non-empty domain.
 
                 remove_labels = current_label_count - last_known;
-                Name star("*");
                 for (size_t i(1); i <= remove_labels; ++ i) {
                     // Construct the name with *
-                    // TODO: Once the underlying DatabaseAccessor takes
-                    // string, do the concatenation on strings, not
-                    // Names
-                    Name superdomain(name.split(i));
-                    Name wildcard(star.concatenate(superdomain));
+                    const Name superdomain(name.split(i));
+                    const string wildcard("*." + superdomain.toText());
+                    const string construct_name(name.toText());
                     // TODO What do we do about DNAME here?
-                    found = getRRset(wildcard, &type, true, false, true,
-                                     &name);
+                    // The types are the same as with original query
+                    found = getRRsets(wildcard, final_types, true,
+                                      &construct_name);
                     if (found.first) {
                         if (first_ns) {
                             // In case we are under NS, we don't
@@ -445,7 +543,42 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
                             // domain, but it could be empty non-terminal. In
                             // that case, we need to cancel the match.
                             records_found = true;
-                            result_rrset = found.second;
+                            const FoundIterator
+                                cni(found.second.find(RRType::CNAME()));
+                            const FoundIterator
+                                nsi(found.second.find(RRType::NS()));
+                            const FoundIterator
+                                nci(found.second.find(RRType::NSEC()));
+                            const FoundIterator wti(found.second.find(type));
+                            if (cni != found.second.end() &&
+                                type != RRType::CNAME()) {
+                                result_rrset = cni->second;
+                                result_status = CNAME;
+                            } else if (nsi != found.second.end()) {
+                                result_rrset = nsi->second;
+                                result_status = DELEGATION;
+                            } else if (wti != found.second.end()) {
+                                result_rrset = wti->second;
+                                result_status = WILDCARD;
+                            } else {
+                                // NXRRSET case in the wildcard
+                                result_status = WILDCARD_NXRRSET;
+                                if (dnssec_data &&
+                                    nci != found.second.end()) {
+                                    // User wants a proof the wildcard doesn't
+                                    // contain it
+                                    //
+                                    // However, we need to get the RRset in the
+                                    // name of the wildcard, not the constructed
+                                    // one, so we walk it again
+                                    found = getRRsets(wildcard, NSEC_TYPES(),
+                                                      true);
+                                    result_rrset =
+                                        found.second.find(RRType::NSEC())->
+                                        second;
+                                }
+                            }
+
                             LOG_DEBUG(logger, DBG_TRACE_DETAILED,
                                       DATASRC_DATABASE_WILDCARD).
                                 arg(accessor_->getDBName()).arg(wildcard).
@@ -457,33 +590,63 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
                                 arg(name).arg(superdomain);
                         }
                         break;
-                    } else if (hasSubdomains(wildcard.toText())) {
+                    } else if (hasSubdomains(wildcard)) {
                         // Empty non-terminal asterisk
                         records_found = true;
                         LOG_DEBUG(logger, DBG_TRACE_DETAILED,
                                   DATASRC_DATABASE_WILDCARD_EMPTY).
                             arg(accessor_->getDBName()).arg(wildcard).
                             arg(name);
+                        if (dnssec_data) {
+                            result_rrset = findNSECCover(Name(wildcard));
+                            if (result_rrset) {
+                                result_status = WILDCARD_NXRRSET;
+                            }
+                        }
                         break;
                     }
                 }
+                // This is the NXDOMAIN case (nothing found anywhere). If
+                // they want DNSSEC data, try getting the NSEC record
+                if (dnssec_data && !records_found) {
+                    get_cover = true;
+                }
+            }
+        } else if (dnssec_data) {
+            // This is the "usual" NXRRSET case
+            // So in case they want DNSSEC, provide the NSEC
+            // (which should be available already here)
+            result_status = NXRRSET;
+            const FoundIterator nci(found.second.find(RRType::NSEC()));
+            if (nci != found.second.end()) {
+                result_rrset = nci->second;
             }
         }
     }
 
     if (!result_rrset) {
-        if (records_found) {
-            logger.debug(DBG_TRACE_DETAILED,
-                         DATASRC_DATABASE_FOUND_NXRRSET)
-                        .arg(accessor_->getDBName()).arg(name)
-                        .arg(getClass()).arg(type);
-            result_status = NXRRSET;
-        } else {
-            logger.debug(DBG_TRACE_DETAILED,
-                         DATASRC_DATABASE_FOUND_NXDOMAIN)
-                        .arg(accessor_->getDBName()).arg(name)
-                        .arg(getClass()).arg(type);
-            result_status = NXDOMAIN;
+        if (result_status == SUCCESS) {
+            // Should we look for NSEC covering the name?
+            if (get_cover) {
+                result_rrset = findNSECCover(name);
+                if (result_rrset) {
+                    result_status = NXDOMAIN;
+                }
+            }
+            // Something is not here and we didn't decide yet what
+            if (records_found) {
+                logger.debug(DBG_TRACE_DETAILED,
+                             DATASRC_DATABASE_FOUND_NXRRSET)
+                    .arg(accessor_->getDBName()).arg(name)
+                    .arg(getClass()).arg(type);
+                result_status = NXRRSET;
+            } else {
+                logger.debug(DBG_TRACE_DETAILED,
+                             DATASRC_DATABASE_FOUND_NXDOMAIN)
+                    .arg(accessor_->getDBName()).arg(name)
+                    .arg(getClass()).arg(type);
+                result_status = NXDOMAIN;
+            }
         }
     } else {
         logger.debug(DBG_TRACE_DETAILED,
@@ -494,6 +657,26 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
 }
 
 Name
+DatabaseClient::Finder::findPreviousName(const Name& name) const {
+    const string str(accessor_->findPreviousName(zone_id_,
+                                                 name.reverse().toText()));
+    try {
+        return (Name(str));
+    }
+    /*
+     * To avoid having the same code many times, we just catch all the
+     * exceptions and handle them in a common code below
+     */
+    catch (const isc::dns::EmptyLabel&) {}
+    catch (const isc::dns::TooLongLabel&) {}
+    catch (const isc::dns::BadLabelType&) {}
+    catch (const isc::dns::BadEscape&) {}
+    catch (const isc::dns::TooLongName&) {}
+    catch (const isc::dns::IncompleteName&) {}
+    isc_throw(DataSourceError, "Bad name " + str + " from findPreviousName");
+}
+
+Name
 DatabaseClient::Finder::getOrigin() const {
     return (origin_);
 }
diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h
index 82918ac..8295779 100644
--- a/src/lib/datasrc/database.h
+++ b/src/lib/datasrc/database.h
@@ -28,6 +28,9 @@
 #include <dns/name.h>
 #include <exceptions/exceptions.h>
 
+#include <map>
+#include <set>
+
 namespace isc {
 namespace datasrc {
 
@@ -471,6 +474,34 @@ public:
      * \return the name of the database
      */
     virtual const std::string& getDBName() const = 0;
+
+    /**
+     * \brief It returns the previous name in DNSSEC order.
+     *
+     * This is used in DatabaseClient::findPreviousName and does more
+     * or less the real work, except for working on strings.
+     *
+     * \param rname The name to ask for previous of, in reversed form.
+     *     We use the reversed form (see isc::dns::Name::reverse),
+     *     because then the case insensitive order of string representation
+     *     and the DNSSEC order correspond (eg. org.example.a is followed
+     *     by org.example.a.b which is followed by org.example.b, etc).
+     * \param zone_id The zone to look through.
+     * \return The previous name.
+     * \note This function must return previous name even in case
+     *     the queried rname does not exist in the zone.
+     * \note This method must skip under-the-zone-cut data (glue data).
+     *     This might be implemented by looking for NSEC records (as glue
+     *     data don't have them) in the zone or in some other way.
+     *
+     * \throw DataSourceError if there's a problem with the database.
+     * \throw NotImplemented if this database doesn't support DNSSEC
+     *     or there's no previous name for the queried one (the NSECs
+     *     might be missing or the queried name is less or equal the
+     *     apex of the zone).
+     */
+    virtual std::string findPreviousName(int zone_id,
+                                         const std::string& rname) const = 0;
 };
 
 /**
@@ -587,6 +618,12 @@ public:
                                 const FindOptions options = FIND_DEFAULT);
 
         /**
+         * \brief Implementation of ZoneFinder::findPreviousName method.
+         */
+        virtual isc::dns::Name findPreviousName(const isc::dns::Name& query)
+            const;
+
+        /**
          * \brief The zone ID
          *
          * This function provides the stored zone ID as passed to the
@@ -609,54 +646,42 @@ public:
         boost::shared_ptr<DatabaseAccessor> accessor_;
         const int zone_id_;
         const isc::dns::Name origin_;
-
+        //
+        /// \brief Shortcut name for the result of getRRsets
+        typedef std::pair<bool, std::map<dns::RRType, dns::RRsetPtr> >
+            FoundRRsets;
+        /// \brief Just shortcut for set of types
+        typedef std::set<dns::RRType> WantedTypes;
         /**
-         * \brief Searches database for an RRset
+         * \brief Searches database for RRsets of one domain.
          *
-         * This method scans RRs of single domain specified by name and finds
-         * RRset with given type or any of redirection RRsets that are
-         * requested.
+         * This method scans RRs of single domain specified by name and
+         * extracts any RRsets found and requested by parameters.
          *
-         * This function is used internally by find(), because this part is
-         * called multiple times with slightly different parameters.
+         * It is used internally by find(), because it is called multiple
+         * times (usually with different domains).
          *
          * \param name Which domain name should be scanned.
-         * \param type The RRType which is requested. This can be NULL, in
-         *     which case the method will look for the redirections only.
-         * \param want_cname If this is true, CNAME redirection may be returned
-         *     instead of the RRset with given type. If there's CNAME and
-         *     something else or the CNAME has multiple RRs, it throws
-         *     DataSourceError.
-         * \param want_dname If this is true, DNAME redirection may be returned
-         *     instead. This is with type = NULL only and is not checked in
-         *     other circumstances. If the DNAME has multiple RRs, it throws
-         *     DataSourceError.
-         * \param want_ns This allows redirection by NS to be returned. If
-         *     any other data is met as well, DataSourceError is thrown.
-         * \param construct_name If set to non-NULL, the resulting RRset will
-         *     be constructed for this name instead of the queried one. This
-         *     is useful for wildcards.
-         * \note It may happen that some of the above error conditions are not
-         *     detected in some circumstances. The goal here is not to validate
-         *     the domain in DB, but to avoid bad behaviour resulting from
-         *     broken data.
-         * \return First part of the result tells if the domain contains any
-         *     RRs. This can be used to decide between NXDOMAIN and NXRRSET.
-         *     The second part is the RRset found (if any) with any relevant
-         *     signatures attached to it.
-         * \todo This interface doesn't look very elegant. Any better idea
-         *     would be nice.
+         * \param types List of types the caller is interested in.
+         * \param check_ns If this is set to true, it checks nothing lives
+         *     together with NS record (with few little exceptions, like RRSIG
+         *     or NSEC). This check is meant for non-apex NS records.
+         * \param construct_name If this is NULL, the resulting RRsets have
+         *     their name set to name. If it is not NULL, it overrides the name
+         *     and uses this one (this can be used for wildcard synthesized
+         *     records).
+         * \return A pair, where the first element indicates if the domain
+         *     contains any RRs at all (not only the requested, it may happen
+         *     this is set to true, but the second part is empty). The second
+         *     part is map from RRtypes to RRsets of the corresponding types.
+         *     If the RRset is not present in DB, the RRtype is not there at
+         *     all (so you'll not find NULL pointer in the result).
+         * \throw DataSourceError If there's a low-level error with the
+         *     database or the database contains bad data.
          */
-        std::pair<bool, isc::dns::RRsetPtr> getRRset(const isc::dns::Name&
-                                                     name,
-                                                     const isc::dns::RRType*
-                                                     type,
-                                                     bool want_cname,
-                                                     bool want_dname,
-                                                     bool want_ns, const
-                                                     isc::dns::Name*
-                                                     construct_name = NULL);
-
+        FoundRRsets getRRsets(const std::string& name,
+                              const WantedTypes& types, bool check_ns,
+                              const std::string* construct_name = NULL);
         /**
          * \brief Checks if something lives below this domain.
          *
@@ -666,6 +691,23 @@ public:
          * \param name The domain to check.
          */
         bool hasSubdomains(const std::string& name);
+
+        /**
+         * \brief Get the NSEC covering a name.
+         *
+         * This one calls findPreviousName on the given name and extracts an NSEC
+         * record on the result. It handles various error cases. The method exists
+         * to share code present at more than one location.
+         */
+        dns::RRsetPtr findNSECCover(const dns::Name& name);
+
+        /**
+         * \brief Convenience type shortcut.
+         *
+         * To find stuff in the result of getRRsets.
+         */
+        typedef std::map<dns::RRType, dns::RRsetPtr>::const_iterator
+            FoundIterator;
     };
 
     /**
diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes
index efb88fd..04ad610 100644
--- a/src/lib/datasrc/datasrc_messages.mes
+++ b/src/lib/datasrc/datasrc_messages.mes
@@ -63,6 +63,11 @@ The maximum allowed number of items of the hotspot cache is set to the given
 number. If there are too many, some of them will be dropped. The size of 0
 means no limit.
 
+% DATASRC_DATABASE_COVER_NSEC_UNSUPPORTED %1 doesn't support DNSSEC when asked for NSEC data covering %2
+The datasource tried to provide an NSEC proof that the named domain does not
+exist, but the database backend doesn't support DNSSEC. No proof is included
+in the answer as a result.
+
 % DATASRC_DATABASE_FIND_RECORDS looking in datasource %1 for record %2/%3
 Debug information. The database data source is looking up records with the given
 name and type in the database.
diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc
index 630b1c0..2e94b67 100644
--- a/src/lib/datasrc/memory_datasrc.cc
+++ b/src/lib/datasrc/memory_datasrc.cc
@@ -661,6 +661,12 @@ InMemoryZoneFinder::getFileName() const {
     return (impl_->file_name_);
 }
 
+isc::dns::Name
+InMemoryZoneFinder::findPreviousName(const isc::dns::Name&) const {
+    isc_throw(NotImplemented, "InMemory data source doesn't support DNSSEC "
+              "yet, can't find previous name");
+}
+
 /// Implementation details for \c InMemoryClient hidden from the public
 /// interface.
 ///
diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h
index c569548..95f589a 100644
--- a/src/lib/datasrc/memory_datasrc.h
+++ b/src/lib/datasrc/memory_datasrc.h
@@ -75,6 +75,12 @@ public:
                             isc::dns::RRsetList* target = NULL,
                             const FindOptions options = FIND_DEFAULT);
 
+    /// \brief Imelementation of the ZoneFinder::findPreviousName method
+    ///
+    /// This one throws NotImplemented exception, as InMemory doesn't
+    /// support DNSSEC currently.
+    virtual isc::dns::Name findPreviousName(const isc::dns::Name& query) const;
+
     /// \brief Inserts an rrset into the zone.
     ///
     /// It puts another RRset into the zone.
diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc
index 956f447..69d5649 100644
--- a/src/lib/datasrc/sqlite3_accessor.cc
+++ b/src/lib/datasrc/sqlite3_accessor.cc
@@ -47,7 +47,8 @@ enum StatementID {
     ADD_RECORD = 7,
     DEL_RECORD = 8,
     ITERATE = 9,
-    NUM_STATEMENTS = 10
+    FIND_PREVIOUS = 10,
+    NUM_STATEMENTS = 11
 };
 
 const char* const text_statements[NUM_STATEMENTS] = {
@@ -68,7 +69,15 @@ const char* const text_statements[NUM_STATEMENTS] = {
     "DELETE FROM records WHERE zone_id=?1 AND name=?2 " // DEL_RECORD
     "AND rdtype=?3 AND rdata=?4",
     "SELECT rdtype, ttl, sigtype, rdata, name FROM records " // ITERATE
-    "WHERE zone_id = ?1 ORDER BY name, rdtype"
+    "WHERE zone_id = ?1 ORDER BY name, rdtype",
+    /*
+     * This one looks for previous name with NSEC record. It is done by
+     * using the reversed name. The NSEC is checked because we need to
+     * skip glue data, which don't have the NSEC.
+     */
+    "SELECT name FROM records " // FIND_PREVIOUS
+    "WHERE zone_id=?1 AND rdtype = 'NSEC' AND "
+    "rname < $2 ORDER BY rname DESC LIMIT 1"
 };
 
 struct SQLite3Parameters {
@@ -391,6 +400,28 @@ SQLite3Accessor::getZone(const std::string& name) const {
     return (std::pair<bool, int>(false, 0));
 }
 
+namespace {
+
+// Conversion to plain char
+const char*
+convertToPlainChar(const unsigned char* ucp, sqlite3 *db) {
+    if (ucp == NULL) {
+        // The field can really be NULL, in which case we return an
+        // empty string, or sqlite may have run out of memory, in
+        // which case we raise an error
+        if (sqlite3_errcode(db) == SQLITE_NOMEM) {
+            isc_throw(DataSourceError,
+                      "Sqlite3 backend encountered a memory allocation "
+                      "error in sqlite3_column_text()");
+        } else {
+            return ("");
+        }
+    }
+    const void* p = ucp;
+    return (static_cast<const char*>(p));
+}
+
+}
 class SQLite3Accessor::Context : public DatabaseAccessor::IteratorContext {
 public:
     // Construct an iterator for all records. When constructed this
@@ -468,7 +499,8 @@ private:
 
     void copyColumn(std::string (&data)[COLUMN_COUNT], int column) {
         data[column] = convertToPlainChar(sqlite3_column_text(statement_,
-                                                              column));
+                                                              column),
+                                          accessor_->dbparameters_->db_);
     }
 
     void bindZoneId(const int zone_id) {
@@ -495,29 +527,6 @@ private:
         statement_ = NULL;
     }
 
-    // This helper method converts from the unsigned char* type (used by
-    // sqlite3) to char* (wanted by std::string). Technically these types
-    // might not be directly convertable
-    // In case sqlite3_column_text() returns NULL, we just make it an
-    // empty string, unless it was caused by a memory error
-    const char* convertToPlainChar(const unsigned char* ucp) {
-        if (ucp == NULL) {
-            // The field can really be NULL, in which case we return an
-            // empty string, or sqlite may have run out of memory, in
-            // which case we raise an error
-            if (sqlite3_errcode(accessor_->dbparameters_->db_)
-                                == SQLITE_NOMEM) {
-                isc_throw(DataSourceError,
-                        "Sqlite3 backend encountered a memory allocation "
-                        "error in sqlite3_column_text()");
-            } else {
-                return ("");
-            }
-        }
-        const void* p = ucp;
-        return (static_cast<const char*>(p));
-    }
-
     const IteratorType iterator_type_;
     boost::shared_ptr<const SQLite3Accessor> accessor_;
     sqlite3_stmt *statement_;
@@ -658,5 +667,49 @@ SQLite3Accessor::deleteRecordInZone(const string (&params)[DEL_PARAM_COUNT]) {
         *dbparameters_, DEL_RECORD, params, "delete record from zone");
 }
 
+std::string
+SQLite3Accessor::findPreviousName(int zone_id, const std::string& rname)
+    const
+{
+    sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS]);
+    sqlite3_clear_bindings(dbparameters_->statements_[FIND_PREVIOUS]);
+
+    if (sqlite3_bind_int(dbparameters_->statements_[FIND_PREVIOUS], 1,
+                         zone_id) != SQLITE_OK) {
+        isc_throw(SQLite3Error, "Could not bind zone ID " << zone_id <<
+                  " to SQL statement (find previous): " <<
+                  sqlite3_errmsg(dbparameters_->db_));
+    }
+    if (sqlite3_bind_text(dbparameters_->statements_[FIND_PREVIOUS], 2,
+                          rname.c_str(), -1, SQLITE_STATIC) != SQLITE_OK) {
+        isc_throw(SQLite3Error, "Could not bind name " << rname <<
+                  " to SQL statement (find previous): " <<
+                  sqlite3_errmsg(dbparameters_->db_));
+    }
+
+    std::string result;
+    const int rc = sqlite3_step(dbparameters_->statements_[FIND_PREVIOUS]);
+    if (rc == SQLITE_ROW) {
+        // We found it
+        result = convertToPlainChar(sqlite3_column_text(dbparameters_->
+            statements_[FIND_PREVIOUS], 0), dbparameters_->db_);
+    }
+    sqlite3_reset(dbparameters_->statements_[FIND_PREVIOUS]);
+
+    if (rc == SQLITE_DONE) {
+        // No NSEC records here, this DB doesn't support DNSSEC or
+        // we asked before the apex
+        isc_throw(isc::NotImplemented, "The zone doesn't support DNSSEC or "
+                  "query before apex");
+    }
+
+    if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
+        // Some kind of error
+        isc_throw(SQLite3Error, "Could not get data for previous name");
+    }
+
+    return (result);
+}
+
 }
 }
diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h
index fae249b..c4bacad 100644
--- a/src/lib/datasrc/sqlite3_accessor.h
+++ b/src/lib/datasrc/sqlite3_accessor.h
@@ -170,6 +170,10 @@ public:
     /// "sqlite3_bind10.sqlite3".
     virtual const std::string& getDBName() const { return (database_name_); }
 
+    /// \brief Concrete implementation of the pure virtual method
+    virtual std::string findPreviousName(int zone_id, const std::string& rname)
+        const;
+
 private:
     /// \brief Private database data
     boost::scoped_ptr<SQLite3Parameters> dbparameters_;
diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc
index 4ed9f12..94ae022 100644
--- a/src/lib/datasrc/tests/database_unittest.cc
+++ b/src/lib/datasrc/tests/database_unittest.cc
@@ -48,9 +48,11 @@ const char* const TEST_RECORDS[][5] = {
     {"www.example.org.", "A", "3600", "", "192.0.2.1"},
     {"www.example.org.", "AAAA", "3600", "", "2001:db8::1"},
     {"www.example.org.", "AAAA", "3600", "", "2001:db8::2"},
+    {"www.example.org.", "NSEC", "3600", "", "www2.example.org. A AAAA NSEC RRSIG"},
+    {"www.example.org.", "RRSIG", "3600", "", "NSEC 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
 
     {"www2.example.org.", "A", "3600", "", "192.0.2.1"},
-    {"www2.example.org.","AAAA", "3600", "", "2001:db8::1"},
+    {"www2.example.org.", "AAAA", "3600", "", "2001:db8::1"},
     {"www2.example.org.", "A", "3600", "", "192.0.2.2"},
 
     {"cname.example.org.", "CNAME", "3600", "", "www.example.org."},
@@ -125,6 +127,7 @@ const char* const TEST_RECORDS[][5] = {
     {"delegation.example.org.", "NS", "3600", "", "ns.example.com."},
     {"delegation.example.org.", "NS", "3600", "",
      "ns.delegation.example.org."},
+    {"delegation.example.org.", "DS", "3600", "", "1 RSAMD5 2 abcd"},
     {"delegation.example.org.", "RRSIG", "3600", "", "NS 5 3 3600 "
      "20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
     {"ns.delegation.example.org.", "A", "3600", "", "192.0.2.1"},
@@ -153,6 +156,9 @@ const char* const TEST_RECORDS[][5] = {
     // doesn't break anything
     {"example.org.", "NS", "3600", "", "ns.example.com."},
     {"example.org.", "A", "3600", "", "192.0.2.1"},
+    {"example.org.", "NSEC", "3600", "", "acnamesig1.example.org. NS A NSEC RRSIG"},
+    {"example.org.", "RRSIG", "3600", "", "NSEC 5 3 3600 20000101000000 "
+              "20000201000000 12345 example.org. FAKEFAKEFAKE"},
     {"example.org.", "RRSIG", "3600", "", "NS 5 3 3600 20000101000000 "
               "20000201000000 12345 example.org. FAKEFAKEFAKE"},
 
@@ -162,11 +168,23 @@ const char* const TEST_RECORDS[][5] = {
     // Something for wildcards
     {"*.wild.example.org.", "A", "3600", "", "192.0.2.5"},
     {"*.wild.example.org.", "RRSIG", "3600", "A", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
+    {"*.wild.example.org.", "NSEC", "3600", "", "cancel.here.wild.example.org. A NSEC RRSIG"},
+    {"*.wild.example.org.", "RRSIG", "3600", "", "NSEC 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
     {"cancel.here.wild.example.org.", "AAAA", "3600", "", "2001:db8::5"},
     {"delegatedwild.example.org.", "NS", "3600", "", "ns.example.com."},
     {"*.delegatedwild.example.org.", "A", "3600", "", "192.0.2.5"},
     {"wild.*.foo.example.org.", "A", "3600", "", "192.0.2.5"},
     {"wild.*.foo.*.bar.example.org.", "A", "3600", "", "192.0.2.5"},
+    {"bao.example.org.", "NSEC", "3600", "", "wild.*.foo.*.bar.example.org. NSEC"},
+    {"*.cnamewild.example.org.", "CNAME", "3600", "", "www.example.org."},
+    {"*.nswild.example.org.", "NS", "3600", "", "ns.example.com."},
+    // For NSEC empty non-terminal
+    {"l.example.org.", "NSEC", "3600", "", "empty.nonterminal.example.org. NSEC"},
+    {"empty.nonterminal.example.org.", "A", "3600", "", "192.0.2.1"},
+    // Invalid rdata
+    {"invalidrdata.example.org.", "A", "3600", "", "Bunch of nonsense"},
+    {"invalidrdata2.example.org.", "A", "3600", "", "192.0.2.1"},
+    {"invalidrdata2.example.org.", "RRSIG", "3600", "", "Nonsense"},
 
     {NULL, NULL, NULL, NULL, NULL},
 };
@@ -223,6 +241,10 @@ public:
                   "This database datasource can't be iterated");
     }
 
+    virtual std::string findPreviousName(int, const std::string&) const {
+        isc_throw(isc::NotImplemented,
+                  "This data source doesn't support DNSSEC");
+    }
 private:
     const std::string database_name_;
 
@@ -529,6 +551,38 @@ public:
         return (latest_clone_);
     }
 
+    virtual std::string findPreviousName(int id, const std::string& rname)
+        const
+    {
+        // Hardcoded for now, but we could compute it from the data
+        // Maybe do it when it is needed some time in future?
+        if (id == -1) {
+            isc_throw(isc::NotImplemented, "Test not implemented behaviour");
+        } else if (id == 42) {
+            if (rname == "org.example.nonterminal.") {
+                return ("l.example.org.");
+            } else if (rname == "org.example.aa.") {
+                return ("example.org.");
+            } else if (rname == "org.example.www2." ||
+                       rname == "org.example.www1.") {
+                return ("www.example.org.");
+            } else if (rname == "org.example.badnsec2.") {
+                return ("badnsec1.example.org.");
+            } else if (rname == "org.example.brokenname.") {
+                return ("brokenname...example.org.");
+            } else if (rname == "org.example.bar.*.") {
+                return ("bao.example.org.");
+            } else if (rname == "org.example.notimplnsec." ||
+                       rname == "org.example.wild.here.") {
+                isc_throw(isc::NotImplemented, "Not implemented in this test");
+            } else {
+                isc_throw(isc::Unexpected, "Unexpected name");
+            }
+        } else {
+            isc_throw(isc::Unexpected, "Unknown zone ID");
+        }
+    }
+
 private:
     // The following member variables are storage and/or update work space
     // of the test zone.  The "master"s are the real objects that contain
@@ -967,21 +1021,25 @@ doFindTest(ZoneFinder& finder,
     ZoneFinder::FindResult result =
         finder.find(name, type, NULL, options);
     ASSERT_EQ(expected_result, result.code) << name << " " << type;
-    if (!expected_rdatas.empty()) {
+    if (!expected_rdatas.empty() && result.rrset) {
         checkRRset(result.rrset, expected_name != Name(".") ? expected_name :
                    name, finder.getClass(), expected_type, expected_ttl,
                    expected_rdatas);
 
-        if (!expected_sig_rdatas.empty()) {
+        if (!expected_sig_rdatas.empty() && result.rrset->getRRsig()) {
             checkRRset(result.rrset->getRRsig(), expected_name != Name(".") ?
                        expected_name : name, finder.getClass(),
                        isc::dns::RRType::RRSIG(), expected_ttl,
                        expected_sig_rdatas);
-        } else {
+        } else if (expected_sig_rdatas.empty()) {
             EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset->getRRsig());
+        } else {
+            ADD_FAILURE() << "Missing RRSIG";
         }
-    } else {
+    } else if (expected_rdatas.empty()) {
         EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset);
+    } else {
+        ADD_FAILURE() << "Missing result";
     }
 }
 
@@ -1422,21 +1480,21 @@ TYPED_TEST(DatabaseClientTest, wildcard) {
                                          "FAKEFAKEFAKE");
     doFindTest(*finder, isc::dns::Name("a.wild.example.org"),
                this->qtype_, this->qtype_, this->rrttl_,
-               ZoneFinder::SUCCESS, this->expected_rdatas_,
+               ZoneFinder::WILDCARD, this->expected_rdatas_,
                this->expected_sig_rdatas_);
     doFindTest(*finder, isc::dns::Name("b.a.wild.example.org"),
-               this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::SUCCESS,
+               this->qtype_, this->qtype_, this->rrttl_, ZoneFinder::WILDCARD,
                this->expected_rdatas_, this->expected_sig_rdatas_);
     this->expected_rdatas_.clear();
     this->expected_sig_rdatas_.clear();
     doFindTest(*finder, isc::dns::Name("a.wild.example.org"),
                isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(),
-               this->rrttl_, ZoneFinder::NXRRSET, this->expected_rdatas_,
-               this->expected_sig_rdatas_);
+               this->rrttl_, ZoneFinder::WILDCARD_NXRRSET,
+               this->expected_rdatas_, this->expected_sig_rdatas_);
     doFindTest(*finder, isc::dns::Name("b.a.wild.example.org"),
                isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(),
-               this->rrttl_, ZoneFinder::NXRRSET, this->expected_rdatas_,
-               this->expected_sig_rdatas_);
+               this->rrttl_, ZoneFinder::WILDCARD_NXRRSET,
+               this->expected_rdatas_, this->expected_sig_rdatas_);
 
     // Direct request for this wildcard
     this->expected_rdatas_.push_back("192.0.2.5");
@@ -1532,11 +1590,146 @@ TYPED_TEST(DatabaseClientTest, wildcard) {
         doFindTest(*finder, isc::dns::Name(*name), this->qtype_,
                    this->qtype_, this->rrttl_, ZoneFinder::NXRRSET,
                    this->expected_rdatas_, this->expected_sig_rdatas_);
+        // FIXME: What should be returned in this case? How does the
+        // DNSSEC logic handle it?
+    }
+
+    const char* negative_dnssec_names[] = {
+        "a.bar.example.org.",
+        "foo.baz.bar.example.org.",
+        "a.foo.bar.example.org.",
+        NULL
+    };
+
+    this->expected_rdatas_.clear();
+    this->expected_rdatas_.push_back("wild.*.foo.*.bar.example.org. NSEC");
+    this->expected_sig_rdatas_.clear();
+    for (const char** name(negative_dnssec_names); *name != NULL; ++ name) {
+        doFindTest(*finder, isc::dns::Name(*name), this->qtype_,
+                   RRType::NSEC(), this->rrttl_, ZoneFinder::WILDCARD_NXRRSET,
+                   this->expected_rdatas_, this->expected_sig_rdatas_,
+                   Name("bao.example.org."), ZoneFinder::FIND_DNSSEC);
+    }
+
+    // Some strange things in the wild node
+    this->expected_rdatas_.clear();
+    this->expected_rdatas_.push_back("www.example.org.");
+    this->expected_sig_rdatas_.clear();
+    doFindTest(*finder, isc::dns::Name("a.cnamewild.example.org."),
+               isc::dns::RRType::TXT(), isc::dns::RRType::CNAME(),
+               this->rrttl_, ZoneFinder::CNAME,
+               this->expected_rdatas_, this->expected_sig_rdatas_);
+
+    this->expected_rdatas_.clear();
+    this->expected_rdatas_.push_back("ns.example.com.");
+    doFindTest(*finder, isc::dns::Name("a.nswild.example.org."),
+               isc::dns::RRType::TXT(), isc::dns::RRType::NS(),
+               this->rrttl_, ZoneFinder::DELEGATION,
+               this->expected_rdatas_, this->expected_sig_rdatas_);
+}
+
+TYPED_TEST(DatabaseClientTest, NXRRSET_NSEC) {
+    // The domain exists, but doesn't have this RRType
+    // So we should get its NSEC
+    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+    this->expected_rdatas_.push_back("www2.example.org. A AAAA NSEC RRSIG");
+    this->expected_sig_rdatas_.push_back("NSEC 5 3 3600 20000101000000 "
+                                         "20000201000000 12345 example.org. "
+                                         "FAKEFAKEFAKE");
+    doFindTest(*finder, isc::dns::Name("www.example.org."),
+               isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(),
+               this->rrttl_, ZoneFinder::NXRRSET,
+               this->expected_rdatas_, this->expected_sig_rdatas_,
+               Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC);
+}
+
+TYPED_TEST(DatabaseClientTest, wildcardNXRRSET_NSEC) {
+    // The domain exists, but doesn't have this RRType
+    // So we should get its NSEC
+    //
+    // The user will have to query us again to get the correct
+    // answer (eg. prove there's not an exact match)
+    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+    this->expected_rdatas_.push_back("cancel.here.wild.example.org. A NSEC "
+                                     "RRSIG");
+    this->expected_sig_rdatas_.push_back("NSEC 5 3 3600 20000101000000 "
+                                         "20000201000000 12345 example.org. "
+                                         "FAKEFAKEFAKE");
+    // Note that the NSEC name should NOT be synthesized.
+    doFindTest(*finder, isc::dns::Name("a.wild.example.org."),
+               isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(),
+               this->rrttl_, ZoneFinder::WILDCARD_NXRRSET,
+               this->expected_rdatas_, this->expected_sig_rdatas_,
+               Name("*.wild.example.org"), ZoneFinder::FIND_DNSSEC);
+}
+
+TYPED_TEST(DatabaseClientTest, NXDOMAIN_NSEC) {
+    // The domain doesn't exist, so we must get the right NSEC
+    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+    this->expected_rdatas_.push_back("www2.example.org. A AAAA NSEC RRSIG");
+    this->expected_sig_rdatas_.push_back("NSEC 5 3 3600 20000101000000 "
+                                         "20000201000000 12345 example.org. "
+                                         "FAKEFAKEFAKE");
+    doFindTest(*finder, isc::dns::Name("www1.example.org."),
+               isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(),
+               this->rrttl_, ZoneFinder::NXDOMAIN,
+               this->expected_rdatas_, this->expected_sig_rdatas_,
+               Name("www.example.org."), ZoneFinder::FIND_DNSSEC);
+    this->expected_rdatas_.clear();
+    this->expected_rdatas_.push_back("acnamesig1.example.org. NS A NSEC RRSIG");
+    // This tests it works correctly in apex (there was a bug, where a check
+    // for NS-alone was there and it would throw).
+    doFindTest(*finder, isc::dns::Name("aa.example.org."),
+               isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(),
+               this->rrttl_, ZoneFinder::NXDOMAIN,
+               this->expected_rdatas_, this->expected_sig_rdatas_,
+               Name("example.org."), ZoneFinder::FIND_DNSSEC);
+
+    // Check that if the DB doesn't support it, the exception from there
+    // is not propagated and it only does not include the NSEC
+    if (!this->is_mock_) {
+        return; // We don't make the real DB to throw
+    }
+    EXPECT_NO_THROW(doFindTest(*finder,
+                               isc::dns::Name("notimplnsec.example.org."),
+                               isc::dns::RRType::TXT(),
+                               isc::dns::RRType::NSEC(), this->rrttl_,
+                               ZoneFinder::NXDOMAIN, this->empty_rdatas_,
+                               this->empty_rdatas_, Name::ROOT_NAME(),
+                               ZoneFinder::FIND_DNSSEC));
+}
+
+TYPED_TEST(DatabaseClientTest, emptyNonterminalNSEC) {
+    // Same as NXDOMAIN_NSEC, but with empty non-terminal
+    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+    this->expected_rdatas_.push_back("empty.nonterminal.example.org. NSEC");
+    doFindTest(*finder, isc::dns::Name("nonterminal.example.org."),
+               isc::dns::RRType::TXT(), isc::dns::RRType::NSEC(), this->rrttl_,
+               ZoneFinder::NXRRSET,
+               this->expected_rdatas_, this->expected_sig_rdatas_,
+               Name("l.example.org."), ZoneFinder::FIND_DNSSEC);
+
+    // Check that if the DB doesn't support it, the exception from there
+    // is not propagated and it only does not include the NSEC
+    if (!this->is_mock_) {
+        return; // We don't make the real DB to throw
     }
+    EXPECT_NO_THROW(doFindTest(*finder,
+                               isc::dns::Name("here.wild.example.org."),
+                               isc::dns::RRType::TXT(),
+                               isc::dns::RRType::NSEC(),
+                               this->rrttl_, ZoneFinder::NXRRSET,
+                               this->empty_rdatas_, this->empty_rdatas_,
+                               Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC));
 }
 
 TYPED_TEST(DatabaseClientTest, getOrigin) {
-    DataSourceClient::FindResult zone(this->client_->findZone(this->zname_));
+    DataSourceClient::FindResult
+        zone(this->client_->findZone(Name("example.org")));
     ASSERT_EQ(result::SUCCESS, zone.code);
     shared_ptr<DatabaseClient::Finder> finder(
         dynamic_pointer_cast<DatabaseClient::Finder>(zone.zone_finder));
@@ -2142,4 +2335,66 @@ TYPED_TEST(DatabaseClientTest, compoundUpdate) {
                ZoneFinder::SUCCESS, this->expected_rdatas_,
                this->empty_rdatas_);
 }
+
+TYPED_TEST(DatabaseClientTest, previous) {
+    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+    EXPECT_EQ(Name("www.example.org."),
+              finder->findPreviousName(Name("www2.example.org.")));
+    // Check a name that doesn't exist there
+    EXPECT_EQ(Name("www.example.org."),
+              finder->findPreviousName(Name("www1.example.org.")));
+    if (this->is_mock_) { // We can't really force the DB to throw
+        // Check it doesn't crash or anything if the underlying DB throws
+        DataSourceClient::FindResult
+            zone(this->client_->findZone(Name("bad.example.org")));
+        finder =
+            dynamic_pointer_cast<DatabaseClient::Finder>(zone.zone_finder);
+
+        EXPECT_THROW(finder->findPreviousName(Name("bad.example.org")),
+                     isc::NotImplemented);
+    } else {
+        // No need to test this on mock one, because we test only that
+        // the exception gets through
+
+        // A name before the origin
+        EXPECT_THROW(finder->findPreviousName(Name("example.com")),
+                     isc::NotImplemented);
+    }
+}
+
+TYPED_TEST(DatabaseClientTest, invalidRdata) {
+    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+    EXPECT_THROW(finder->find(Name("invalidrdata.example.org."), RRType::A()),
+                 DataSourceError);
+    EXPECT_THROW(finder->find(Name("invalidrdata2.example.org."), RRType::A()),
+                 DataSourceError);
+}
+
+TEST_F(MockDatabaseClientTest, missingNSEC) {
+    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+    /*
+     * FIXME: For now, we can't really distinguish this bogus input
+     * from not-signed zone so we can't throw. But once we can,
+     * enable the original test.
+     */
+#if 0
+    EXPECT_THROW(finder->find(Name("badnsec2.example.org."), RRType::A(), NULL,
+                              ZoneFinder::FIND_DNSSEC),
+                 DataSourceError);
+#endif
+    doFindTest(*finder, Name("badnsec2.example.org."), RRType::A(),
+               RRType::A(), this->rrttl_, ZoneFinder::NXDOMAIN,
+               this->expected_rdatas_, this->expected_sig_rdatas_);
+}
+
+TEST_F(MockDatabaseClientTest, badName) {
+    shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+    EXPECT_THROW(finder->findPreviousName(Name("brokenname.example.org.")),
+                 DataSourceError);
+}
+
 }
diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
index a926935..2b854db 100644
--- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc
+++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
@@ -395,6 +395,14 @@ public:
 };
 
 /**
+ * \brief Check that findPreviousName throws as it should now.
+ */
+TEST_F(InMemoryZoneFinderTest, findPreviousName) {
+    EXPECT_THROW(zone_finder_.findPreviousName(Name("www.example.org")),
+                 isc::NotImplemented);
+}
+
+/**
  * \brief Test InMemoryZoneFinder::InMemoryZoneFinder constructor.
  *
  * Takes the created zone finder and checks its properties they are the same
diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
index 8b423f8..3974977 100644
--- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
+++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
@@ -351,6 +351,45 @@ TEST_F(SQLite3AccessorTest, getRecords) {
     EXPECT_FALSE(context->getNext(columns));
 }
 
+TEST_F(SQLite3AccessorTest, findPrevious) {
+    EXPECT_EQ("dns01.example.com.",
+              accessor->findPreviousName(1, "com.example.dns02."));
+    // A name that doesn't exist
+    EXPECT_EQ("dns01.example.com.",
+              accessor->findPreviousName(1, "com.example.dns01x."));
+    // Largest name
+    EXPECT_EQ("www.example.com.",
+              accessor->findPreviousName(1, "com.example.wwww"));
+    // Out of zone after the last name
+    EXPECT_EQ("www.example.com.",
+              accessor->findPreviousName(1, "org.example."));
+    // Case insensitive?
+    EXPECT_EQ("dns01.example.com.",
+              accessor->findPreviousName(1, "com.exaMple.DNS02."));
+    // A name that doesn't exist
+    EXPECT_EQ("dns01.example.com.",
+              accessor->findPreviousName(1, "com.exaMple.DNS01X."));
+    // The DB contains foo.bar.example.com., which would be in between
+    // these two names. However, that one does not have an NSEC record,
+    // which is how this database recognizes glue data, so it should
+    // be skipped.
+    EXPECT_EQ("example.com.",
+              accessor->findPreviousName(1, "com.example.cname-ext."));
+    // Throw when we are before the origin
+    EXPECT_THROW(accessor->findPreviousName(1, "com.example."),
+                 isc::NotImplemented);
+    EXPECT_THROW(accessor->findPreviousName(1, "a.example."),
+                 isc::NotImplemented);
+}
+
+TEST_F(SQLite3AccessorTest, findPreviousNoData) {
+    // This one doesn't hold any NSEC records, so it shouldn't work
+    // The underlying DB/data don't support DNSSEC, so it's not implemented
+    // (does it make sense? Or different exception here?)
+    EXPECT_THROW(accessor->findPreviousName(3, "com.example.sql2.www."),
+                 isc::NotImplemented);
+}
+
 // Test fixture for creating a db that automatically deletes it before start,
 // and when done
 class SQLite3Create : public ::testing::Test {
diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h
index bb4f435..c83b14b 100644
--- a/src/lib/datasrc/zone.h
+++ b/src/lib/datasrc/zone.h
@@ -54,13 +54,50 @@ public:
     ///
     /// Note: the codes are tentative.  We may need more, or we may find
     /// some of them unnecessary as we implement more details.
+    ///
+    /// Some are synonyms of others in terms of RCODE returned to user.
+    /// But they help the logic to decide if it should ask for a NSEC
+    /// that covers something or not (for example, in case of NXRRSET,
+    /// the directly returned NSEC is sufficient, but with wildcard one,
+    /// we need to add one proving there's no exact match and this is
+    /// actually the best wildcard we have). Data sources that don't
+    /// support DNSSEC don't need to distinguish them.
+    ///
+    /// In case of NXRRSET related results, the returned NSEC record
+    /// belongs to the domain which would provide the result if it
+    /// contained the correct type (in case of NXRRSET, it is the queried
+    /// domain, in case of WILDCARD_NXRRSET, it is the wildcard domain
+    /// that matched the query name). In case of an empty nonterminal,
+    /// an NSEC is provided for the interval where the empty nonterminal
+    /// lives. The end of the interval is the subdomain causing existence
+    /// of the empty nonterminal (if there's sub.x.example.com, and no record
+    /// in x.example.com, then x.example.com exists implicitly - is the empty
+    /// nonterminal and sub.x.example.com is the subdomain causing it).
+    ///
+    /// Examples: if zone "example.com" has the following record:
+    /// \code
+    /// a.b.example.com. NSEC c.example.com.
+    /// \endcode
+    /// a call to \c find() for "b.example.com." will result in NXRRSET,
+    /// and if the FIND_DNSSEC option is set this NSEC will be returned.
+    /// Likewise, if zone "example.org" has the following record,
+    /// \code
+    /// x.*.example.org. NSEC a.example.org.
+    /// \endcode
+    /// a call to \c find() for "y.example.org" will result in
+    /// WILDCARD_NXRRSET (*.example.org is an empty nonterminal wildcard node),
+    /// and if the FIND_DNSSEC option is set this NSEC will be returned.
+    ///
+    /// In case of NXDOMAIN, the returned NSEC covers the queried domain.
     enum Result {
         SUCCESS,                ///< An exact match is found.
         DELEGATION,             ///< The search encounters a zone cut.
         NXDOMAIN, ///< There is no domain name that matches the search name
         NXRRSET,  ///< There is a matching name but no RRset of the search type
         CNAME,    ///< The search encounters and returns a CNAME RR
-        DNAME     ///< The search encounters and returns a DNAME RR
+        DNAME,    ///< The search encounters and returns a DNAME RR
+        WILDCARD, ///< Succes by wildcard match, for DNSSEC
+        WILDCARD_NXRRSET ///< NXRRSET on wildcard, for DNSSEC
     };
 
     /// A helper structure to represent the search result of \c find().
@@ -135,7 +172,7 @@ public:
     //@}
 
     ///
-    /// \name Search Method
+    /// \name Search Methods
     ///
     //@{
     /// Search the zone for a given pair of domain name and RR type.
@@ -167,8 +204,8 @@ public:
     ///   We should revisit the interface before we heavily rely on it.
     ///
     /// The \c options parameter specifies customized behavior of the search.
-    /// Their semantics is as follows:
-    /// - \c GLUE_OK Allow search under a zone cut.  By default the search
+    /// Their semantics is as follows (they are or bit-field):
+    /// - \c FIND_GLUE_OK Allow search under a zone cut.  By default the search
     ///   will stop once it encounters a zone cut.  If this option is specified
     ///   it remembers information about the highest zone cut and continues
     ///   the search until it finds an exact match for the given name or it
@@ -176,6 +213,9 @@ public:
     ///   RRsets for that name are searched just like the normal case;
     ///   otherwise, if the search has encountered a zone cut, \c DELEGATION
     ///   with the information of the highest zone cut will be returned.
+    /// - \c FIND_DNSSEC Request that DNSSEC data (like NSEC, RRSIGs) are
+    ///   returned with the answer. It is allowed for the data source to
+    ///   include them even when not requested.
     ///
     /// A derived version of this method may involve internal resource
     /// allocation, especially for constructing the resulting RRset, and may
@@ -195,6 +235,31 @@ public:
                             isc::dns::RRsetList* target = NULL,
                             const FindOptions options
                             = FIND_DEFAULT) = 0;
+
+    /// \brief Get previous name in the zone
+    ///
+    /// Gets the previous name in the DNSSEC order. This can be used
+    /// to find the correct NSEC records for proving nonexistence
+    /// of domains.
+    ///
+    /// The concrete implementation might throw anything it thinks appropriate,
+    /// however it is recommended to stick to the ones listed here. The user
+    /// of this method should be able to handle any exceptions.
+    ///
+    /// This method does not include under-zone-cut data (glue data).
+    ///
+    /// \param query The name for which one we look for a previous one. The
+    ///     queried name doesn't have to exist in the zone.
+    /// \return The preceding name
+    ///
+    /// \throw NotImplemented in case the data source backend doesn't support
+    ///     DNSSEC or there is no previous in the zone (NSEC records might be
+    ///     missing in the DB, the queried name is less or equal to the apex).
+    /// \throw DataSourceError for low-level or internal datasource errors
+    ///     (like broken connection to database, wrong data living there).
+    /// \throw std::bad_alloc For allocation errors.
+    virtual isc::dns::Name findPreviousName(const isc::dns::Name& query)
+        const = 0;
     //@}
 };
 
diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am
index 3a23e23..3d4a663 100644
--- a/src/lib/dns/Makefile.am
+++ b/src/lib/dns/Makefile.am
@@ -30,6 +30,8 @@ EXTRA_DIST += rdata/generic/dnskey_48.cc
 EXTRA_DIST += rdata/generic/dnskey_48.h
 EXTRA_DIST += rdata/generic/ds_43.cc
 EXTRA_DIST += rdata/generic/ds_43.h
+EXTRA_DIST += rdata/generic/hinfo_13.cc
+EXTRA_DIST += rdata/generic/hinfo_13.h
 EXTRA_DIST += rdata/generic/mx_15.cc
 EXTRA_DIST += rdata/generic/mx_15.h
 EXTRA_DIST += rdata/generic/naptr_35.cc
@@ -101,6 +103,7 @@ libdns___la_SOURCES += tsig.h tsig.cc
 libdns___la_SOURCES += tsigerror.h tsigerror.cc
 libdns___la_SOURCES += tsigkey.h tsigkey.cc
 libdns___la_SOURCES += tsigrecord.h tsigrecord.cc
+libdns___la_SOURCES += character_string.h character_string.cc
 libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.h
 libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.cc
 libdns___la_SOURCES += rdata/generic/detail/txt_like.h
diff --git a/src/lib/dns/character_string.cc b/src/lib/dns/character_string.cc
new file mode 100644
index 0000000..3a289ac
--- /dev/null
+++ b/src/lib/dns/character_string.cc
@@ -0,0 +1,140 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include "character_string.h"
+#include "rdata.h"
+
+using namespace std;
+using namespace isc::dns::rdata;
+
+namespace isc {
+namespace dns {
+
+namespace {
+bool isDigit(char c) {
+    return (('0' <= c) && (c <= '9'));
+}
+}
+
+std::string
+characterstr::getNextCharacterString(const std::string& input_str,
+                              std::string::const_iterator& input_iterator)
+{
+    string result;
+
+    // If the input string only contains white-spaces, it is an invalid
+    // <character-string>
+    if (input_iterator >= input_str.end()) {
+        isc_throw(InvalidRdataText, "Invalid text format, \
+                  <character-string> field is missing.");
+    }
+
+    // Whether the <character-string> is separated with double quotes (")
+    bool quotes_separated = (*input_iterator == '"');
+    // Whether the quotes are pared if the string is quotes separated
+    bool quotes_paired = false;
+
+    if (quotes_separated) {
+        ++input_iterator;
+    }
+
+    while(input_iterator < input_str.end()){
+        // Escaped characters processing
+        if (*input_iterator == '\\') {
+            if (input_iterator + 1 == input_str.end()) {
+                isc_throw(InvalidRdataText, "<character-string> ended \
+                          prematurely.");
+            } else {
+                if (isDigit(*(input_iterator + 1))) {
+                    // \DDD where each D is a digit. It its the octet
+                    // corresponding to the decimal number described by DDD
+                    if (input_iterator + 3 >= input_str.end()) {
+                        isc_throw(InvalidRdataText, "<character-string> ended \
+                                  prematurely.");
+                    } else {
+                        int n = 0;
+                        ++input_iterator;
+                        for (int i = 0; i < 3; ++i) {
+                            if (isDigit(*input_iterator)) {
+                                n = n*10 + (*input_iterator - '0');
+                                ++input_iterator;
+                            } else {
+                                isc_throw(InvalidRdataText, "Illegal decimal \
+                                          escaping series");
+                            }
+                        }
+                        if (n > 255) {
+                            isc_throw(InvalidRdataText, "Illegal octet \
+                                      number");
+                        }
+                        result.push_back(n);
+                        continue;
+                    }
+                } else {
+                    ++input_iterator;
+                    result.push_back(*input_iterator);
+                    ++input_iterator;
+                    continue;
+                }
+            }
+        }
+
+        if (quotes_separated) {
+            // If the <character-string> is seperated with quotes symbol and
+            // another quotes symbol is encountered, it is the end of the
+            // <character-string>
+            if (*input_iterator == '"') {
+                quotes_paired = true;
+                ++input_iterator;
+                // Reach the end of character string
+                break;
+            }
+        } else if (*input_iterator == ' ') {
+            // If the <character-string> is not seperated with quotes symbol,
+            // it is seperated with <space> char
+            break;
+        }
+
+        result.push_back(*input_iterator);
+
+        ++input_iterator;
+    }
+
+    if (result.size() > MAX_CHARSTRING_LEN) {
+        isc_throw(CharStringTooLong, "<character-string> is too long");
+    }
+
+    if (quotes_separated && !quotes_paired) {
+        isc_throw(InvalidRdataText, "The quotes are not paired");
+    }
+
+    return (result);
+}
+
+std::string
+characterstr::getNextCharacterString(util::InputBuffer& buffer, size_t len) {
+    uint8_t str_len = buffer.readUint8();
+
+    size_t pos = buffer.getPosition();
+    if (len - pos < str_len) {
+        isc_throw(InvalidRdataLength, "Invalid string length");
+    }
+
+    uint8_t buf[MAX_CHARSTRING_LEN];
+    buffer.readData(buf, str_len);
+    return (string(buf, buf + str_len));
+}
+
+} // end of namespace dns
+} // end of namespace isc
diff --git a/src/lib/dns/character_string.h b/src/lib/dns/character_string.h
new file mode 100644
index 0000000..7961274
--- /dev/null
+++ b/src/lib/dns/character_string.h
@@ -0,0 +1,57 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __CHARACTER_STRING_H
+#define __CHARACTER_STRING_H
+
+#include <string>
+#include <exceptions/exceptions.h>
+#include <util/buffer.h>
+
+namespace isc {
+namespace dns {
+
+// \brief Some utility functions to extract <character-string> from string
+// or InputBuffer
+//
+// <character-string> is expressed in one or two ways: as a contiguous set
+// of characters without interior spaces, or as a string beginning with a "
+// and ending with a ".  Inside a " delimited string any character can
+// occur, except for a " itself, which must be quoted using \ (back slash).
+// Ref. RFC1035
+
+
+namespace characterstr {
+    /// Get a <character-string> from a string
+    ///
+    /// \param input_str The input string
+    /// \param input_iterator The iterator from which to start extracting,
+    ///        the iterator will be updated to new position after the function
+    ///        is returned
+    /// \return A std::string that contains the extracted <character-string>
+    std::string getNextCharacterString(const std::string& input_str,
+                                       std::string::const_iterator& input_iterator);
+
+    /// Get a <character-string> from a input buffer
+    ///
+    /// \param buffer The input buffer
+    /// \param len The input buffer total length
+    /// \return A std::string that contains the extracted <character-string>
+    std::string getNextCharacterString(util::InputBuffer& buffer, size_t len);
+
+} // namespace characterstr
+} // namespace dns
+} // namespace isc
+
+#endif // __CHARACTER_STRING_H
diff --git a/src/lib/dns/python/Makefile.am b/src/lib/dns/python/Makefile.am
index 6c4ef54..4452e40 100644
--- a/src/lib/dns/python/Makefile.am
+++ b/src/lib/dns/python/Makefile.am
@@ -4,40 +4,46 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
-pyexec_LTLIBRARIES = pydnspp.la
-pydnspp_la_SOURCES = pydnspp.cc pydnspp_common.cc pydnspp_towire.h
-pydnspp_la_SOURCES += name_python.cc name_python.h
-pydnspp_la_SOURCES += messagerenderer_python.cc messagerenderer_python.h
-pydnspp_la_SOURCES += rcode_python.cc rcode_python.h
-pydnspp_la_SOURCES += tsigkey_python.cc tsigkey_python.h
-pydnspp_la_SOURCES += tsigerror_python.cc tsigerror_python.h
-pydnspp_la_SOURCES += tsig_rdata_python.cc tsig_rdata_python.h
-pydnspp_la_SOURCES += tsigrecord_python.cc tsigrecord_python.h
-pydnspp_la_SOURCES += tsig_python.cc tsig_python.h
+lib_LTLIBRARIES = libpydnspp.la
+libpydnspp_la_SOURCES = pydnspp_common.cc pydnspp_common.h pydnspp_towire.h
+libpydnspp_la_SOURCES += name_python.cc name_python.h
+libpydnspp_la_SOURCES += rrset_python.cc rrset_python.h
+libpydnspp_la_SOURCES += rrclass_python.cc rrclass_python.h
+libpydnspp_la_SOURCES += rrtype_python.cc rrtype_python.h
+libpydnspp_la_SOURCES += rrttl_python.cc rrttl_python.h
+libpydnspp_la_SOURCES += rdata_python.cc rdata_python.h
+libpydnspp_la_SOURCES += messagerenderer_python.cc messagerenderer_python.h
+libpydnspp_la_SOURCES += rcode_python.cc rcode_python.h
+libpydnspp_la_SOURCES += opcode_python.cc opcode_python.h
+libpydnspp_la_SOURCES += question_python.cc question_python.h
+libpydnspp_la_SOURCES += tsigkey_python.cc tsigkey_python.h
+libpydnspp_la_SOURCES += tsigerror_python.cc tsigerror_python.h
+libpydnspp_la_SOURCES += tsig_rdata_python.cc tsig_rdata_python.h
+libpydnspp_la_SOURCES += tsigrecord_python.cc tsigrecord_python.h
+libpydnspp_la_SOURCES += tsig_python.cc tsig_python.h
+libpydnspp_la_SOURCES += edns_python.cc edns_python.h
+libpydnspp_la_SOURCES += message_python.cc message_python.h
+
+libpydnspp_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
+libpydnspp_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
+libpydnspp_la_LDFLAGS = $(PYTHON_LDFLAGS)
+
 
+
+pyexec_LTLIBRARIES = pydnspp.la
+pydnspp_la_SOURCES = pydnspp.cc
 pydnspp_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
 # Note: PYTHON_CXXFLAGS may have some -Wno... workaround, which must be
 # placed after -Wextra defined in AM_CXXFLAGS
 pydnspp_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
 pydnspp_la_LDFLAGS = $(PYTHON_LDFLAGS)
 
-# directly included from source files, so these don't have their own
-# rules
-EXTRA_DIST = pydnspp_common.h
-EXTRA_DIST += edns_python.cc
-EXTRA_DIST += message_python.cc
-EXTRA_DIST += rrclass_python.cc
-EXTRA_DIST += opcode_python.cc
-EXTRA_DIST += rrset_python.cc
-EXTRA_DIST += question_python.cc
-EXTRA_DIST += rrttl_python.cc
-EXTRA_DIST += rdata_python.cc
-EXTRA_DIST += rrtype_python.cc
-EXTRA_DIST += tsigerror_python_inc.cc
+EXTRA_DIST = tsigerror_python_inc.cc
 
 # Python prefers .so, while some OSes (specifically MacOS) use a different
 # suffix for dynamic objects.  -module is necessary to work this around.
 pydnspp_la_LDFLAGS += -module
 pydnspp_la_LIBADD = $(top_builddir)/src/lib/dns/libdns++.la
 pydnspp_la_LIBADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+pydnspp_la_LIBADD += libpydnspp.la
 pydnspp_la_LIBADD += $(PYTHON_LIB)
diff --git a/src/lib/dns/python/edns_python.cc b/src/lib/dns/python/edns_python.cc
index 83c3bfa..8f0f1a4 100644
--- a/src/lib/dns/python/edns_python.cc
+++ b/src/lib/dns/python/edns_python.cc
@@ -12,38 +12,38 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <Python.h>
+
 #include <cassert>
 
 #include <dns/edns.h>
+#include <dns/exceptions.h>
+#include <dns/messagerenderer.h>
+#include <util/python/pycppwrapper_util.h>
+
+#include "edns_python.h"
+#include "name_python.h"
+#include "rrclass_python.h"
+#include "rrtype_python.h"
+#include "rrttl_python.h"
+#include "rdata_python.h"
+#include "messagerenderer_python.h"
+#include "pydnspp_common.h"
 
 using namespace isc::dns;
-using namespace isc::util;
 using namespace isc::dns::rdata;
-
-//
-// Definition of the classes
-//
-
-// For each class, we need a struct, a helper functions (init, destroy,
-// and static wrappers around the methods we export), a list of methods,
-// and a type description
+using namespace isc::dns::python;
+using namespace isc::util;
+using namespace isc::util::python;
 
 namespace {
-//
-// EDNS
-//
-
-// The s_* Class simply covers one instantiation of the object
 
 class s_EDNS : public PyObject {
 public:
-    EDNS* edns;
+    EDNS* cppobj;
 };
 
-//
-// We declare the functions here, the definitions are below
-// the type definition of the object, since both can use the other
-//
+typedef CPPPyObjectContainer<s_EDNS, EDNS> EDNSContainer;
 
 // General creation and destruction
 int EDNS_init(s_EDNS* self, PyObject* args);
@@ -103,60 +103,6 @@ PyMethodDef EDNS_methods[] = {
     { NULL, NULL, 0, NULL }
 };
 
-// This defines the complete type for reflection in python and
-// parsing of PyObject* to s_EDNS
-// Most of the functions are not actually implemented and NULL here.
-PyTypeObject edns_type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "pydnspp.EDNS",
-    sizeof(s_EDNS),                     // tp_basicsize
-    0,                                  // tp_itemsize
-    (destructor)EDNS_destroy,           // tp_dealloc
-    NULL,                               // tp_print
-    NULL,                               // tp_getattr
-    NULL,                               // tp_setattr
-    NULL,                               // tp_reserved
-    NULL,                               // tp_repr
-    NULL,                               // tp_as_number
-    NULL,                               // tp_as_sequence
-    NULL,                               // tp_as_mapping
-    NULL,                               // tp_hash 
-    NULL,                               // tp_call
-    EDNS_str,                           // tp_str
-    NULL,                               // tp_getattro
-    NULL,                               // tp_setattro
-    NULL,                               // tp_as_buffer
-    Py_TPFLAGS_DEFAULT,                 // tp_flags
-    "The EDNS class encapsulates DNS extensions "
-    "provided by the EDNSx protocol.",
-    NULL,                               // tp_traverse
-    NULL,                               // tp_clear
-    NULL,                               // tp_richcompare
-    0,                                  // tp_weaklistoffset
-    NULL,                               // tp_iter
-    NULL,                               // tp_iternext
-    EDNS_methods,                       // tp_methods
-    NULL,                               // tp_members
-    NULL,                               // tp_getset
-    NULL,                               // tp_base
-    NULL,                               // tp_dict
-    NULL,                               // tp_descr_get
-    NULL,                               // tp_descr_set
-    0,                                  // tp_dictoffset
-    (initproc)EDNS_init,                // tp_init
-    NULL,                               // tp_alloc
-    PyType_GenericNew,                  // tp_new
-    NULL,                               // tp_free
-    NULL,                               // tp_is_gc
-    NULL,                               // tp_bases
-    NULL,                               // tp_mro
-    NULL,                               // tp_cache
-    NULL,                               // tp_subclasses
-    NULL,                               // tp_weaklist
-    NULL,                               // tp_del
-    0                                   // tp_version_tag
-};
-
 EDNS*
 createFromRR(const Name& name, const RRClass& rrclass, const RRType& rrtype,
              const RRTTL& rrttl, const Rdata& rdata, uint8_t& extended_rcode)
@@ -179,15 +125,15 @@ createFromRR(const Name& name, const RRClass& rrclass, const RRType& rrtype,
 int
 EDNS_init(s_EDNS* self, PyObject* args) {
     uint8_t version = EDNS::SUPPORTED_VERSION;
-    const s_Name* name;
-    const s_RRClass* rrclass;
-    const s_RRType* rrtype;
-    const s_RRTTL* rrttl;
-    const s_Rdata* rdata;
+    const PyObject* name;
+    const PyObject* rrclass;
+    const PyObject* rrtype;
+    const PyObject* rrttl;
+    const PyObject* rdata;
 
     if (PyArg_ParseTuple(args, "|b", &version)) {
         try {
-            self->edns = new EDNS(version);
+            self->cppobj = new EDNS(version);
         } catch (const isc::InvalidParameter& ex) {
             PyErr_SetString(po_InvalidParameter, ex.what());
             return (-1);
@@ -203,10 +149,12 @@ EDNS_init(s_EDNS* self, PyObject* args) {
         // in this context so that we can share the try-catch logic with
         // EDNS_createFromRR() (see below).
         uint8_t extended_rcode;
-        self->edns = createFromRR(*name->cppobj, *rrclass->rrclass,
-                                  *rrtype->rrtype, *rrttl->rrttl,
-                                  *rdata->rdata, extended_rcode);
-        return (self->edns != NULL ? 0 : -1);
+        self->cppobj = createFromRR(PyName_ToName(name),
+                                    PyRRClass_ToRRClass(rrclass),
+                                    PyRRType_ToRRType(rrtype),
+                                    PyRRTTL_ToRRTTL(rrttl),
+                                    PyRdata_ToRdata(rdata), extended_rcode);
+        return (self->cppobj != NULL ? 0 : -1);
     }
 
     PyErr_Clear();
@@ -217,19 +165,19 @@ EDNS_init(s_EDNS* self, PyObject* args) {
 
 void
 EDNS_destroy(s_EDNS* const self) {
-    delete self->edns;
-    self->edns = NULL;
+    delete self->cppobj;
+    self->cppobj = NULL;
     Py_TYPE(self)->tp_free(self);
 }
 
 PyObject*
 EDNS_toText(const s_EDNS* const self) {
     // Py_BuildValue makes python objects from native data
-    return (Py_BuildValue("s", self->edns->toText().c_str()));
+    return (Py_BuildValue("s", self->cppobj->toText().c_str()));
 }
 
 PyObject*
-EDNS_str(PyObject* const self) {
+EDNS_str(PyObject* self) {
     // Simply call the to_text method we already defined
     return (PyObject_CallMethod(self,
                                 const_cast<char*>("to_text"),
@@ -240,14 +188,14 @@ PyObject*
 EDNS_toWire(const s_EDNS* const self, PyObject* args) {
     PyObject* bytes;
     uint8_t extended_rcode;
-    s_MessageRenderer* renderer;
+    PyObject* renderer;
 
     if (PyArg_ParseTuple(args, "Ob", &bytes, &extended_rcode) &&
         PySequence_Check(bytes)) {
         PyObject* bytes_o = bytes;
-        
+
         OutputBuffer buffer(0);
-        self->edns->toWire(buffer, extended_rcode);
+        self->cppobj->toWire(buffer, extended_rcode);
         PyObject* rd_bytes = PyBytes_FromStringAndSize(
             static_cast<const char*>(buffer.getData()), buffer.getLength());
         PyObject* result = PySequence_InPlaceConcat(bytes_o, rd_bytes);
@@ -257,8 +205,8 @@ EDNS_toWire(const s_EDNS* const self, PyObject* args) {
         return (result);
     } else if (PyArg_ParseTuple(args, "O!b", &messagerenderer_type,
                                 &renderer, &extended_rcode)) {
-        const unsigned int n = self->edns->toWire(*renderer->messagerenderer,
-                                                  extended_rcode);
+        const unsigned int n = self->cppobj->toWire(
+            PyMessageRenderer_ToMessageRenderer(renderer), extended_rcode);
 
         return (Py_BuildValue("I", n));
     }
@@ -269,12 +217,12 @@ EDNS_toWire(const s_EDNS* const self, PyObject* args) {
 
 PyObject*
 EDNS_getVersion(const s_EDNS* const self) {
-    return (Py_BuildValue("B", self->edns->getVersion()));
+    return (Py_BuildValue("B", self->cppobj->getVersion()));
 }
 
 PyObject*
 EDNS_getDNSSECAwareness(const s_EDNS* const self) {
-    if (self->edns->getDNSSECAwareness()) {
+    if (self->cppobj->getDNSSECAwareness()) {
         Py_RETURN_TRUE;
     } else {
         Py_RETURN_FALSE;
@@ -287,13 +235,13 @@ EDNS_setDNSSECAwareness(s_EDNS* self, PyObject* args) {
     if (!PyArg_ParseTuple(args, "O!", &PyBool_Type, &b)) {
         return (NULL);
     }
-    self->edns->setDNSSECAwareness(b == Py_True);
+    self->cppobj->setDNSSECAwareness(b == Py_True);
     Py_RETURN_NONE;
 }
 
 PyObject*
 EDNS_getUDPSize(const s_EDNS* const self) {
-    return (Py_BuildValue("I", self->edns->getUDPSize()));
+    return (Py_BuildValue("I", self->cppobj->getUDPSize()));
 }
 
 PyObject*
@@ -310,17 +258,17 @@ EDNS_setUDPSize(s_EDNS* self, PyObject* args) {
                         "UDP size is not an unsigned 16-bit integer");
         return (NULL);
     }
-    self->edns->setUDPSize(size);
+    self->cppobj->setUDPSize(size);
     Py_RETURN_NONE;
 }
 
 PyObject*
 EDNS_createFromRR(const s_EDNS* null_self, PyObject* args) {
-    const s_Name* name;
-    const s_RRClass* rrclass;
-    const s_RRType* rrtype;
-    const s_RRTTL* rrttl;
-    const s_Rdata* rdata;
+    const PyObject* name;
+    const PyObject* rrclass;
+    const PyObject* rrtype;
+    const PyObject* rrttl;
+    const PyObject* rdata;
     s_EDNS* edns_obj = NULL;
 
     assert(null_self == NULL);
@@ -334,14 +282,17 @@ EDNS_createFromRR(const s_EDNS* null_self, PyObject* args) {
             return (NULL);
         }
 
-        edns_obj->edns = createFromRR(*name->cppobj, *rrclass->rrclass,
-                                      *rrtype->rrtype, *rrttl->rrttl,
-                                      *rdata->rdata, extended_rcode);
-        if (edns_obj->edns != NULL) {
+        edns_obj->cppobj = createFromRR(PyName_ToName(name),
+                                        PyRRClass_ToRRClass(rrclass),
+                                        PyRRType_ToRRType(rrtype),
+                                        PyRRTTL_ToRRTTL(rrttl),
+                                        PyRdata_ToRdata(rdata),
+                                        extended_rcode);
+        if (edns_obj->cppobj != NULL) {
             PyObject* extrcode_obj = Py_BuildValue("B", extended_rcode);
             return (Py_BuildValue("OO", edns_obj, extrcode_obj));
         }
-        
+
         Py_DECREF(edns_obj);
         return (NULL);
     }
@@ -353,23 +304,90 @@ EDNS_createFromRR(const s_EDNS* null_self, PyObject* args) {
 }
 
 } // end of anonymous namespace
-// end of EDNS
 
-// Module Initialization, all statics are initialized here
+namespace isc {
+namespace dns {
+namespace python {
+
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_EDNS
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject edns_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "pydnspp.EDNS",
+    sizeof(s_EDNS),                     // tp_basicsize
+    0,                                  // tp_itemsize
+    (destructor)EDNS_destroy,           // tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash
+    NULL,                               // tp_call
+    EDNS_str,                           // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    "The EDNS class encapsulates DNS extensions "
+    "provided by the EDNSx protocol.",
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    NULL,                               // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    EDNS_methods,                       // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    (initproc)EDNS_init,                // tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
+
+PyObject*
+createEDNSObject(const EDNS& source) {
+    EDNSContainer container(PyObject_New(s_EDNS, &edns_type));
+    container.set(new EDNS(source));
+    return (container.release());
+}
+
 bool
-initModulePart_EDNS(PyObject* mod) {
-    // We initialize the static description object with PyType_Ready(),
-    // then add it to the module. This is not just a check! (leaving
-    // this out results in segmentation faults)
-    if (PyType_Ready(&edns_type) < 0) {
-        return (false);
+PyEDNS_Check(PyObject* obj) {
+    if (obj == NULL) {
+        isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck");
     }
-    Py_INCREF(&edns_type);
-    void* p = &edns_type;
-    PyModule_AddObject(mod, "EDNS", static_cast<PyObject*>(p));
-
-    addClassVariable(edns_type, "SUPPORTED_VERSION",
-                     Py_BuildValue("B", EDNS::SUPPORTED_VERSION));
+    return (PyObject_TypeCheck(obj, &edns_type));
+}
 
-    return (true);
+const EDNS&
+PyEDNS_ToEDNS(const PyObject* edns_obj) {
+    if (edns_obj == NULL) {
+        isc_throw(PyCPPWrapperException,
+                  "obj argument NULL in EDNS PyObject conversion");
+    }
+    const s_EDNS* edns = static_cast<const s_EDNS*>(edns_obj);
+    return (*edns->cppobj);
 }
+
+} // end namespace python
+} // end namespace dns
+} // end namespace isc
diff --git a/src/lib/dns/python/edns_python.h b/src/lib/dns/python/edns_python.h
new file mode 100644
index 0000000..30d92ab
--- /dev/null
+++ b/src/lib/dns/python/edns_python.h
@@ -0,0 +1,64 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_EDNS_H
+#define __PYTHON_EDNS_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace dns {
+class EDNS;
+
+namespace python {
+
+extern PyTypeObject edns_type;
+
+/// This is a simple shortcut to create a python EDNS object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called within a try block
+/// followed by necessary setup for python exception.
+PyObject* createEDNSObject(const EDNS& source);
+
+/// \brief Checks if the given python object is a EDNS object
+///
+/// \exception PyCPPWrapperException if obj is NULL
+///
+/// \param obj The object to check the type of
+/// \return true if the object is of type EDNS, false otherwise
+bool PyEDNS_Check(PyObject* obj);
+
+/// \brief Returns a reference to the EDNS object contained within the given
+///        Python object.
+///
+/// \note The given object MUST be of type EDNS; this can be checked with
+///       either the right call to ParseTuple("O!"), or with PyEDNS_Check()
+///
+/// \note This is not a copy; if the EDNS is needed when the PyObject
+/// may be destroyed, the caller must copy it itself.
+///
+/// \param edns_obj The edns object to convert
+const EDNS& PyEDNS_ToEDNS(const PyObject* edns_obj);
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_EDNS_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc
index 00596f8..b40ab45 100644
--- a/src/lib/dns/python/message_python.cc
+++ b/src/lib/dns/python/message_python.cc
@@ -12,49 +12,39 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#define PY_SSIZE_T_CLEAN
+#include <Python.h>
+
 #include <exceptions/exceptions.h>
 #include <dns/message.h>
 #include <dns/rcode.h>
 #include <dns/tsig.h>
-
+#include <dns/exceptions.h>
+#include <dns/messagerenderer.h>
+
+#include "name_python.h"
+#include "question_python.h"
+#include "edns_python.h"
+#include "rcode_python.h"
+#include "opcode_python.h"
+#include "rrset_python.h"
+#include "message_python.h"
+#include "messagerenderer_python.h"
+#include "tsig_python.h"
+#include "tsigrecord_python.h"
+#include "pydnspp_common.h"
+
+using namespace std;
 using namespace isc::dns;
+using namespace isc::dns::python;
 using namespace isc::util;
 
 namespace {
-//
-// Declaration of the custom exceptions
-// Initialization and addition of these go in the initModulePart
-// function at the end of this file
-//
-PyObject* po_MessageTooShort;
-PyObject* po_InvalidMessageSection;
-PyObject* po_InvalidMessageOperation;
-PyObject* po_InvalidMessageUDPSize;
-
-//
-// Definition of the classes
-//
-
-// For each class, we need a struct, a helper functions (init, destroy,
-// and static wrappers around the methods we export), a list of methods,
-// and a type description
-
-//
-// Message
-//
-
-// The s_* Class simply coverst one instantiation of the object
 class s_Message : public PyObject {
 public:
-    Message* message;
+    isc::dns::Message* cppobj;
 };
 
-//
-// We declare the functions here, the definitions are below
-// the type definition of the object, since both can use the other
-//
-
-// General creation and destruction
 int Message_init(s_Message* self, PyObject* args);
 void Message_destroy(s_Message* self);
 
@@ -178,59 +168,6 @@ PyMethodDef Message_methods[] = {
     { NULL, NULL, 0, NULL }
 };
 
-// This defines the complete type for reflection in python and
-// parsing of PyObject* to s_Message
-// Most of the functions are not actually implemented and NULL here.
-PyTypeObject message_type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "pydnspp.Message",
-    sizeof(s_Message),                  // tp_basicsize
-    0,                                  // tp_itemsize
-    (destructor)Message_destroy,        // tp_dealloc
-    NULL,                               // tp_print
-    NULL,                               // tp_getattr
-    NULL,                               // tp_setattr
-    NULL,                               // tp_reserved
-    NULL,                               // tp_repr
-    NULL,                               // tp_as_number
-    NULL,                               // tp_as_sequence
-    NULL,                               // tp_as_mapping
-    NULL,                               // tp_hash 
-    NULL,                               // tp_call
-    Message_str,                        // tp_str
-    NULL,                               // tp_getattro
-    NULL,                               // tp_setattro
-    NULL,                               // tp_as_buffer
-    Py_TPFLAGS_DEFAULT,                 // tp_flags
-    "The Message class encapsulates a standard DNS message.",
-    NULL,                               // tp_traverse
-    NULL,                               // tp_clear
-    NULL,                               // tp_richcompare
-    0,                                  // tp_weaklistoffset
-    NULL,                               // tp_iter
-    NULL,                               // tp_iternext
-    Message_methods,                    // tp_methods
-    NULL,                               // tp_members
-    NULL,                               // tp_getset
-    NULL,                               // tp_base
-    NULL,                               // tp_dict
-    NULL,                               // tp_descr_get
-    NULL,                               // tp_descr_set
-    0,                                  // tp_dictoffset
-    (initproc)Message_init,             // tp_init
-    NULL,                               // tp_alloc
-    PyType_GenericNew,                  // tp_new
-    NULL,                               // tp_free
-    NULL,                               // tp_is_gc
-    NULL,                               // tp_bases
-    NULL,                               // tp_mro
-    NULL,                               // tp_cache
-    NULL,                               // tp_subclasses
-    NULL,                               // tp_weaklist
-    NULL,                               // tp_del
-    0                                   // tp_version_tag
-};
-
 int
 Message_init(s_Message* self, PyObject* args) {
     int i;
@@ -238,10 +175,10 @@ Message_init(s_Message* self, PyObject* args) {
     if (PyArg_ParseTuple(args, "i", &i)) {
         PyErr_Clear();
         if (i == Message::PARSE) {
-            self->message = new Message(Message::PARSE);
+            self->cppobj = new Message(Message::PARSE);
             return (0);
         } else if (i == Message::RENDER) {
-            self->message = new Message(Message::RENDER);
+            self->cppobj = new Message(Message::RENDER);
             return (0);
         } else {
             PyErr_SetString(PyExc_TypeError, "Message mode must be Message.PARSE or Message.RENDER");
@@ -256,8 +193,8 @@ Message_init(s_Message* self, PyObject* args) {
 
 void
 Message_destroy(s_Message* self) {
-    delete self->message;
-    self->message = NULL;
+    delete self->cppobj;
+    self->cppobj = NULL;
     Py_TYPE(self)->tp_free(self);
 }
 
@@ -271,7 +208,7 @@ Message_getHeaderFlag(s_Message* self, PyObject* args) {
         return (NULL);
     }
 
-    if (self->message->getHeaderFlag(
+    if (self->cppobj->getHeaderFlag(
             static_cast<Message::HeaderFlag>(messageflag))) {
         Py_RETURN_TRUE;
     } else {
@@ -296,7 +233,7 @@ Message_setHeaderFlag(s_Message* self, PyObject* args) {
     }
 
     try {
-        self->message->setHeaderFlag(
+        self->cppobj->setHeaderFlag(
             static_cast<Message::HeaderFlag>(messageflag), on == Py_True);
         Py_RETURN_NONE;
     } catch (const InvalidMessageOperation& imo) {
@@ -312,7 +249,7 @@ Message_setHeaderFlag(s_Message* self, PyObject* args) {
 
 PyObject*
 Message_getQid(s_Message* self) {
-    return (Py_BuildValue("I", self->message->getQid()));
+    return (Py_BuildValue("I", self->cppobj->getQid()));
 }
 
 PyObject*
@@ -331,7 +268,7 @@ Message_setQid(s_Message* self, PyObject* args) {
     }
 
     try {
-        self->message->setQid(id);
+        self->cppobj->setQid(id);
         Py_RETURN_NONE;
     } catch (const InvalidMessageOperation& imo) {
         PyErr_SetString(po_InvalidMessageOperation, imo.what());
@@ -341,35 +278,25 @@ Message_setQid(s_Message* self, PyObject* args) {
 
 PyObject*
 Message_getRcode(s_Message* self) {
-    s_Rcode* rcode;
-
-    rcode = static_cast<s_Rcode*>(rcode_type.tp_alloc(&rcode_type, 0));
-    if (rcode != NULL) {
-        rcode->cppobj = NULL;
-        try {
-            rcode->cppobj = new Rcode(self->message->getRcode());
-        } catch (const InvalidMessageOperation& imo) {
-            PyErr_SetString(po_InvalidMessageOperation, imo.what());
-        } catch (...) {
-            PyErr_SetString(po_IscException, "Unexpected exception");
-        }
-        if (rcode->cppobj == NULL) {
-            Py_DECREF(rcode);
-            return (NULL);
-        }
+    try {
+        return (createRcodeObject(self->cppobj->getRcode()));
+    } catch (const InvalidMessageOperation& imo) {
+        PyErr_SetString(po_InvalidMessageOperation, imo.what());
+        return (NULL);
+    } catch (...) {
+        PyErr_SetString(po_IscException, "Unexpected exception");
+        return (NULL);
     }
-
-    return (rcode);
 }
 
 PyObject*
 Message_setRcode(s_Message* self, PyObject* args) {
-    s_Rcode* rcode;
+    PyObject* rcode;
     if (!PyArg_ParseTuple(args, "O!", &rcode_type, &rcode)) {
         return (NULL);
     }
     try {
-        self->message->setRcode(*rcode->cppobj);
+        self->cppobj->setRcode(PyRcode_ToRcode(rcode));
         Py_RETURN_NONE;
     } catch (const InvalidMessageOperation& imo) {
         PyErr_SetString(po_InvalidMessageOperation, imo.what());
@@ -379,35 +306,31 @@ Message_setRcode(s_Message* self, PyObject* args) {
 
 PyObject*
 Message_getOpcode(s_Message* self) {
-    s_Opcode* opcode;
-
-    opcode = static_cast<s_Opcode*>(opcode_type.tp_alloc(&opcode_type, 0));
-    if (opcode != NULL) {
-        opcode->opcode = NULL;
-        try {
-            opcode->opcode = new Opcode(self->message->getOpcode());
-        } catch (const InvalidMessageOperation& imo) {
-            PyErr_SetString(po_InvalidMessageOperation, imo.what());
-        } catch (...) {
-            PyErr_SetString(po_IscException, "Unexpected exception");
-        }
-        if (opcode->opcode == NULL) {
-            Py_DECREF(opcode);
-            return (NULL);
-        }
+    try {
+        return (createOpcodeObject(self->cppobj->getOpcode()));
+    } catch (const InvalidMessageOperation& imo) {
+        PyErr_SetString(po_InvalidMessageOperation, imo.what());
+        return (NULL);
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Failed to get message opcode: " + string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+        return (NULL);
+    } catch (...) {
+        PyErr_SetString(po_IscException,
+                        "Unexpected exception getting opcode from message");
+        return (NULL);
     }
-
-    return (opcode);
 }
 
 PyObject*
 Message_setOpcode(s_Message* self, PyObject* args) {
-    s_Opcode* opcode;
+    PyObject* opcode;
     if (!PyArg_ParseTuple(args, "O!", &opcode_type, &opcode)) {
         return (NULL);
     }
     try {
-        self->message->setOpcode(*opcode->opcode);
+        self->cppobj->setOpcode(PyOpcode_ToOpcode(opcode));
         Py_RETURN_NONE;
     } catch (const InvalidMessageOperation& imo) {
         PyErr_SetString(po_InvalidMessageOperation, imo.what());
@@ -417,32 +340,31 @@ Message_setOpcode(s_Message* self, PyObject* args) {
 
 PyObject*
 Message_getEDNS(s_Message* self) {
-    s_EDNS* edns;
-    EDNS* edns_body;
-    ConstEDNSPtr src = self->message->getEDNS();
-
+    ConstEDNSPtr src = self->cppobj->getEDNS();
     if (!src) {
         Py_RETURN_NONE;
     }
-    if ((edns_body = new(nothrow) EDNS(*src)) == NULL) {
-        return (PyErr_NoMemory());
-    }
-    edns = static_cast<s_EDNS*>(opcode_type.tp_alloc(&edns_type, 0));
-    if (edns != NULL) {
-        edns->edns = edns_body;
+    try {
+        return (createEDNSObject(*src));
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Failed to get EDNS from message: " + string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure getting EDNS from message");
     }
-
-    return (edns);
+    return (NULL);
 }
 
 PyObject*
 Message_setEDNS(s_Message* self, PyObject* args) {
-    s_EDNS* edns;
+    PyObject* edns;
     if (!PyArg_ParseTuple(args, "O!", &edns_type, &edns)) {
         return (NULL);
     }
     try {
-        self->message->setEDNS(EDNSPtr(new EDNS(*edns->edns)));
+        self->cppobj->setEDNS(EDNSPtr(new EDNS(PyEDNS_ToEDNS(edns))));
         Py_RETURN_NONE;
     } catch (const InvalidMessageOperation& imo) {
         PyErr_SetString(po_InvalidMessageOperation, imo.what());
@@ -453,7 +375,7 @@ Message_setEDNS(s_Message* self, PyObject* args) {
 PyObject*
 Message_getTSIGRecord(s_Message* self) {
     try {
-        const TSIGRecord* tsig_record = self->message->getTSIGRecord();
+        const TSIGRecord* tsig_record = self->cppobj->getTSIGRecord();
 
         if (tsig_record == NULL) {
             Py_RETURN_NONE;
@@ -483,7 +405,7 @@ Message_getRRCount(s_Message* self, PyObject* args) {
         return (NULL);
     }
     try {
-        return (Py_BuildValue("I", self->message->getRRCount(
+        return (Py_BuildValue("I", self->cppobj->getRRCount(
                                   static_cast<Message::Section>(section))));
     } catch (const isc::OutOfRange& ex) {
         PyErr_SetString(PyExc_OverflowError, ex.what());
@@ -496,8 +418,8 @@ PyObject*
 Message_getQuestion(s_Message* self) {
     QuestionIterator qi, qi_end;
     try {
-        qi = self->message->beginQuestion();
-        qi_end = self->message->endQuestion();
+        qi = self->cppobj->beginQuestion();
+        qi_end = self->cppobj->endQuestion();
     } catch (const InvalidMessageSection& ex) {
         PyErr_SetString(po_InvalidMessageSection, ex.what());
         return (NULL);
@@ -512,23 +434,25 @@ Message_getQuestion(s_Message* self) {
         return (NULL);
     }
 
-    for (; qi != qi_end; ++qi) {
-        s_Question *question = static_cast<s_Question*>(
-            question_type.tp_alloc(&question_type, 0));
-        if (question == NULL) {
-            Py_DECREF(question);
-            Py_DECREF(list);
-            return (NULL);
-        }
-        question->question = *qi;
-        if (PyList_Append(list, question) == -1) {
-            Py_DECREF(question);
-            Py_DECREF(list);
-            return (NULL);
+    try {
+        for (; qi != qi_end; ++qi) {
+            if (PyList_Append(list, createQuestionObject(**qi)) == -1) {
+                Py_DECREF(list);
+                return (NULL);
+            }
         }
-        Py_DECREF(question);
+        return (list);
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Unexpected failure getting Question section: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure getting Question section");
     }
-    return (list);
+    Py_DECREF(list);
+    return (NULL);
 }
 
 PyObject*
@@ -542,9 +466,9 @@ Message_getSection(s_Message* self, PyObject* args) {
     }
     RRsetIterator rrsi, rrsi_end;
     try {
-        rrsi = self->message->beginSection(
+        rrsi = self->cppobj->beginSection(
             static_cast<Message::Section>(section));
-        rrsi_end = self->message->endSection(
+        rrsi_end = self->cppobj->endSection(
             static_cast<Message::Section>(section));
     } catch (const isc::OutOfRange& ex) {
         PyErr_SetString(PyExc_OverflowError, ex.what());
@@ -562,25 +486,25 @@ Message_getSection(s_Message* self, PyObject* args) {
     if (list == NULL) {
         return (NULL);
     }
-    for (; rrsi != rrsi_end; ++rrsi) {
-        s_RRset *rrset = static_cast<s_RRset*>(
-            rrset_type.tp_alloc(&rrset_type, 0));
-        if (rrset == NULL) {
-                Py_DECREF(rrset);
-                Py_DECREF(list);
-                return (NULL);
-        }
-        rrset->rrset = *rrsi;
-        if (PyList_Append(list, rrset) == -1) {
-                Py_DECREF(rrset);
-                Py_DECREF(list);
-                return (NULL);
+    try {
+        for (; rrsi != rrsi_end; ++rrsi) {
+            if (PyList_Append(list, createRRsetObject(**rrsi)) == -1) {
+                    Py_DECREF(list);
+                    return (NULL);
+            }
         }
-        // PyList_Append increases refcount, so we remove ours since
-        // we don't need it anymore
-        Py_DECREF(rrset);
+        return (list);
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Unexpected failure creating Question object: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure creating Question object");
     }
-    return (list);
+    Py_DECREF(list);
+    return (NULL);
 }
 
 //static PyObject* Message_beginQuestion(s_Message* self, PyObject* args);
@@ -590,14 +514,14 @@ Message_getSection(s_Message* self, PyObject* args) {
 //static PyObject* Message_addQuestion(s_Message* self, PyObject* args);
 PyObject*
 Message_addQuestion(s_Message* self, PyObject* args) {
-    s_Question *question;
+    PyObject* question;
 
     if (!PyArg_ParseTuple(args, "O!", &question_type, &question)) {
         return (NULL);
     }
 
-    self->message->addQuestion(question->question);
-    
+    self->cppobj->addQuestion(PyQuestion_ToQuestion(question));
+
     Py_RETURN_NONE;
 }
 
@@ -605,15 +529,15 @@ PyObject*
 Message_addRRset(s_Message* self, PyObject* args) {
     PyObject *sign = Py_False;
     int section;
-    s_RRset* rrset;
+    PyObject* rrset;
     if (!PyArg_ParseTuple(args, "iO!|O!", &section, &rrset_type, &rrset,
                           &PyBool_Type, &sign)) {
         return (NULL);
     }
 
     try {
-        self->message->addRRset(static_cast<Message::Section>(section),
-                                rrset->rrset, sign == Py_True);
+        self->cppobj->addRRset(static_cast<Message::Section>(section),
+                               PyRRset_ToRRsetPtr(rrset), sign == Py_True);
         Py_RETURN_NONE;
     } catch (const InvalidMessageOperation& imo) {
         PyErr_SetString(po_InvalidMessageOperation, imo.what());
@@ -634,10 +558,10 @@ Message_clear(s_Message* self, PyObject* args) {
     if (PyArg_ParseTuple(args, "i", &i)) {
         PyErr_Clear();
         if (i == Message::PARSE) {
-            self->message->clear(Message::PARSE);
+            self->cppobj->clear(Message::PARSE);
             Py_RETURN_NONE;
         } else if (i == Message::RENDER) {
-            self->message->clear(Message::RENDER);
+            self->cppobj->clear(Message::RENDER);
             Py_RETURN_NONE;
         } else {
             PyErr_SetString(PyExc_TypeError,
@@ -651,7 +575,7 @@ Message_clear(s_Message* self, PyObject* args) {
 
 PyObject*
 Message_makeResponse(s_Message* self) {
-    self->message->makeResponse();
+    self->cppobj->makeResponse();
     Py_RETURN_NONE;
 }
 
@@ -659,7 +583,7 @@ PyObject*
 Message_toText(s_Message* self) {
     // Py_BuildValue makes python objects from native data
     try {
-        return (Py_BuildValue("s", self->message->toText().c_str()));
+        return (Py_BuildValue("s", self->cppobj->toText().c_str()));
     } catch (const InvalidMessageOperation& imo) {
         PyErr_Clear();
         PyErr_SetString(po_InvalidMessageOperation, imo.what());
@@ -680,16 +604,17 @@ Message_str(PyObject* self) {
 
 PyObject*
 Message_toWire(s_Message* self, PyObject* args) {
-    s_MessageRenderer* mr;
-    s_TSIGContext* tsig_ctx = NULL;
-    
+    PyObject* mr;
+    PyObject* tsig_ctx = NULL;
+
     if (PyArg_ParseTuple(args, "O!|O!", &messagerenderer_type, &mr,
                          &tsigcontext_type, &tsig_ctx)) {
         try {
             if (tsig_ctx == NULL) {
-                self->message->toWire(*mr->messagerenderer);
+                self->cppobj->toWire(PyMessageRenderer_ToMessageRenderer(mr));
             } else {
-                self->message->toWire(*mr->messagerenderer, *tsig_ctx->cppobj);
+                self->cppobj->toWire(PyMessageRenderer_ToMessageRenderer(mr),
+                                     PyTSIGContext_ToTSIGContext(tsig_ctx));
             }
             // If we return NULL it is seen as an error, so use this for
             // None returns
@@ -727,10 +652,10 @@ Message_fromWire(s_Message* self, PyObject* args) {
     if (!PyArg_ParseTuple(args, "y#", &b, &len)) {
         return (NULL);
     }
-    
+
     InputBuffer inbuf(b, len);
     try {
-        self->message->fromWire(inbuf);
+        self->cppobj->fromWire(inbuf);
         Py_RETURN_NONE;
     } catch (const InvalidMessageOperation& imo) {
         PyErr_SetString(po_InvalidMessageOperation, imo.what());
@@ -747,71 +672,75 @@ Message_fromWire(s_Message* self, PyObject* args) {
     }
 }
 
-// Module Initialization, all statics are initialized here
-bool
-initModulePart_Message(PyObject* mod) {
-    if (PyType_Ready(&message_type) < 0) {
-        return (false);
-    }
-    Py_INCREF(&message_type);
-    
-    // Class variables
-    // These are added to the tp_dict of the type object
-    //
-    addClassVariable(message_type, "PARSE",
-                     Py_BuildValue("I", Message::PARSE));
-    addClassVariable(message_type, "RENDER",
-                     Py_BuildValue("I", Message::RENDER));
-
-    addClassVariable(message_type, "HEADERFLAG_QR",
-                     Py_BuildValue("I", Message::HEADERFLAG_QR));
-    addClassVariable(message_type, "HEADERFLAG_AA",
-                     Py_BuildValue("I", Message::HEADERFLAG_AA));
-    addClassVariable(message_type, "HEADERFLAG_TC",
-                     Py_BuildValue("I", Message::HEADERFLAG_TC));
-    addClassVariable(message_type, "HEADERFLAG_RD",
-                     Py_BuildValue("I", Message::HEADERFLAG_RD));
-    addClassVariable(message_type, "HEADERFLAG_RA",
-                     Py_BuildValue("I", Message::HEADERFLAG_RA));
-    addClassVariable(message_type, "HEADERFLAG_AD",
-                     Py_BuildValue("I", Message::HEADERFLAG_AD));
-    addClassVariable(message_type, "HEADERFLAG_CD",
-                     Py_BuildValue("I", Message::HEADERFLAG_CD));
-
-    addClassVariable(message_type, "SECTION_QUESTION",
-                     Py_BuildValue("I", Message::SECTION_QUESTION));
-    addClassVariable(message_type, "SECTION_ANSWER",
-                     Py_BuildValue("I", Message::SECTION_ANSWER));
-    addClassVariable(message_type, "SECTION_AUTHORITY",
-                     Py_BuildValue("I", Message::SECTION_AUTHORITY));
-    addClassVariable(message_type, "SECTION_ADDITIONAL",
-                     Py_BuildValue("I", Message::SECTION_ADDITIONAL));
-
-    addClassVariable(message_type, "DEFAULT_MAX_UDPSIZE",
-                     Py_BuildValue("I", Message::DEFAULT_MAX_UDPSIZE));
-
-    /* Class-specific exceptions */
-    po_MessageTooShort = PyErr_NewException("pydnspp.MessageTooShort", NULL,
-                                            NULL);
-    PyModule_AddObject(mod, "MessageTooShort", po_MessageTooShort);
-    po_InvalidMessageSection =
-        PyErr_NewException("pydnspp.InvalidMessageSection", NULL, NULL);
-    PyModule_AddObject(mod, "InvalidMessageSection", po_InvalidMessageSection);
-    po_InvalidMessageOperation =
-        PyErr_NewException("pydnspp.InvalidMessageOperation", NULL, NULL);
-    PyModule_AddObject(mod, "InvalidMessageOperation",
-                       po_InvalidMessageOperation);
-    po_InvalidMessageUDPSize =
-        PyErr_NewException("pydnspp.InvalidMessageUDPSize", NULL, NULL);
-    PyModule_AddObject(mod, "InvalidMessageUDPSize", po_InvalidMessageUDPSize);
-    po_DNSMessageBADVERS = PyErr_NewException("pydnspp.DNSMessageBADVERS",
-                                              NULL, NULL);
-    PyModule_AddObject(mod, "DNSMessageBADVERS", po_DNSMessageBADVERS);
-
-    PyModule_AddObject(mod, "Message",
-                       reinterpret_cast<PyObject*>(&message_type));
-
-
-    return (true);
-}
 } // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+
+//
+// Declaration of the custom exceptions
+// Initialization and addition of these go in the initModulePart
+// function in pydnspp.cc
+//
+PyObject* po_MessageTooShort;
+PyObject* po_InvalidMessageSection;
+PyObject* po_InvalidMessageOperation;
+PyObject* po_InvalidMessageUDPSize;
+
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_Message
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject message_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "pydnspp.Message",
+    sizeof(s_Message),                  // tp_basicsize
+    0,                                  // tp_itemsize
+    (destructor)Message_destroy,        // tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash
+    NULL,                               // tp_call
+    Message_str,                        // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    "The Message class encapsulates a standard DNS message.",
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    NULL,                               // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    Message_methods,                    // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    (initproc)Message_init,             // tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
+
+} // end python namespace
+} // end dns namespace
+} // end isc namespace
diff --git a/src/lib/dns/python/message_python.h b/src/lib/dns/python/message_python.h
new file mode 100644
index 0000000..be23890
--- /dev/null
+++ b/src/lib/dns/python/message_python.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_MESSAGE_H
+#define __PYTHON_MESSAGE_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace dns {
+class Message;
+
+namespace python {
+
+extern PyObject* po_MessageTooShort;
+extern PyObject* po_InvalidMessageSection;
+extern PyObject* po_InvalidMessageOperation;
+extern PyObject* po_InvalidMessageUDPSize;
+
+extern PyTypeObject message_type;
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_MESSAGE_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/messagerenderer_python.cc b/src/lib/dns/python/messagerenderer_python.cc
index e6f5d3e..bb89622 100644
--- a/src/lib/dns/python/messagerenderer_python.cc
+++ b/src/lib/dns/python/messagerenderer_python.cc
@@ -17,6 +17,7 @@
 #include <util/buffer.h>
 
 #include <dns/messagerenderer.h>
+#include <util/python/pycppwrapper_util.h>
 
 #include "pydnspp_common.h"
 #include "messagerenderer_python.h"
@@ -24,15 +25,21 @@
 using namespace isc::dns;
 using namespace isc::dns::python;
 using namespace isc::util;
-
-// MessageRenderer
-
-s_MessageRenderer::s_MessageRenderer() : outputbuffer(NULL),
-                                         messagerenderer(NULL)
-{
-}
+using namespace isc::util::python;
 
 namespace {
+// The s_* Class simply covers one instantiation of the object.
+//
+// since we don't use *Buffer in the python version (but work with
+// the already existing bytearray type where we use these custom buffers
+// in C++, we need to keep track of one here.
+class s_MessageRenderer : public PyObject {
+public:
+    s_MessageRenderer();
+    isc::util::OutputBuffer* outputbuffer;
+    MessageRenderer* cppobj;
+};
+
 int MessageRenderer_init(s_MessageRenderer* self);
 void MessageRenderer_destroy(s_MessageRenderer* self);
 
@@ -72,15 +79,15 @@ PyMethodDef MessageRenderer_methods[] = {
 int
 MessageRenderer_init(s_MessageRenderer* self) {
     self->outputbuffer = new OutputBuffer(4096);
-    self->messagerenderer = new MessageRenderer(*self->outputbuffer);
+    self->cppobj = new MessageRenderer(*self->outputbuffer);
     return (0);
 }
 
 void
 MessageRenderer_destroy(s_MessageRenderer* self) {
-    delete self->messagerenderer;
+    delete self->cppobj;
     delete self->outputbuffer;
-    self->messagerenderer = NULL;
+    self->cppobj = NULL;
     self->outputbuffer = NULL;
     Py_TYPE(self)->tp_free(self);
 }
@@ -88,18 +95,18 @@ MessageRenderer_destroy(s_MessageRenderer* self) {
 PyObject*
 MessageRenderer_getData(s_MessageRenderer* self) {
     return (Py_BuildValue("y#",
-                         self->messagerenderer->getData(),
-                          self->messagerenderer->getLength()));
+                          self->cppobj->getData(),
+                          self->cppobj->getLength()));
 }
 
 PyObject*
 MessageRenderer_getLength(s_MessageRenderer* self) {
-    return (Py_BuildValue("I", self->messagerenderer->getLength()));
+    return (Py_BuildValue("I", self->cppobj->getLength()));
 }
 
 PyObject*
 MessageRenderer_isTruncated(s_MessageRenderer* self) {
-    if (self->messagerenderer->isTruncated()) {
+    if (self->cppobj->isTruncated()) {
         Py_RETURN_TRUE;
     } else {
         Py_RETURN_FALSE;
@@ -108,17 +115,17 @@ MessageRenderer_isTruncated(s_MessageRenderer* self) {
 
 PyObject*
 MessageRenderer_getLengthLimit(s_MessageRenderer* self) {
-    return (Py_BuildValue("I", self->messagerenderer->getLengthLimit()));
+    return (Py_BuildValue("I", self->cppobj->getLengthLimit()));
 }
 
 PyObject*
 MessageRenderer_getCompressMode(s_MessageRenderer* self) {
-    return (Py_BuildValue("I", self->messagerenderer->getCompressMode()));
+    return (Py_BuildValue("I", self->cppobj->getCompressMode()));
 }
 
 PyObject*
 MessageRenderer_setTruncated(s_MessageRenderer* self) {
-    self->messagerenderer->setTruncated();
+    self->cppobj->setTruncated();
     Py_RETURN_NONE;
 }
 
@@ -138,7 +145,7 @@ MessageRenderer_setLengthLimit(s_MessageRenderer* self,
                         "MessageRenderer length limit out of range");
         return (NULL);
     }
-    self->messagerenderer->setLengthLimit(lengthlimit);
+    self->cppobj->setLengthLimit(lengthlimit);
     Py_RETURN_NONE;
 }
 
@@ -152,12 +159,12 @@ MessageRenderer_setCompressMode(s_MessageRenderer* self,
     }
 
     if (mode == MessageRenderer::CASE_INSENSITIVE) {
-        self->messagerenderer->setCompressMode(MessageRenderer::CASE_INSENSITIVE);
+        self->cppobj->setCompressMode(MessageRenderer::CASE_INSENSITIVE);
         // If we return NULL it is seen as an error, so use this for
         // None returns, it also applies to CASE_SENSITIVE.
         Py_RETURN_NONE;
     } else if (mode == MessageRenderer::CASE_SENSITIVE) {
-        self->messagerenderer->setCompressMode(MessageRenderer::CASE_SENSITIVE);
+        self->cppobj->setCompressMode(MessageRenderer::CASE_SENSITIVE);
         Py_RETURN_NONE;
     } else {
         PyErr_SetString(PyExc_TypeError,
@@ -169,12 +176,11 @@ MessageRenderer_setCompressMode(s_MessageRenderer* self,
 
 PyObject*
 MessageRenderer_clear(s_MessageRenderer* self) {
-    self->messagerenderer->clear();
+    self->cppobj->clear();
     Py_RETURN_NONE;
 }
 } // end of unnamed namespace
 
-// end of MessageRenderer
 namespace isc {
 namespace dns {
 namespace python {
@@ -233,37 +239,29 @@ PyTypeObject messagerenderer_type = {
     0                                   // tp_version_tag
 };
 
-// Module Initialization, all statics are initialized here
-bool
-initModulePart_MessageRenderer(PyObject* mod) {
-    // Add the exceptions to the module
+// If we need a createMessageRendererObject(), should we copy? can we?
+// copy the existing buffer into a new one, then create a new renderer with
+// that buffer?
 
-    // Add the enums to the module
-
-    // Add the constants to the module
-
-    // Add the classes to the module
-    // We initialize the static description object with PyType_Ready(),
-    // then add it to the module
+bool
+PyMessageRenderer_Check(PyObject* obj) {
+    if (obj == NULL) {
+        isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck");
+    }
+    return (PyObject_TypeCheck(obj, &messagerenderer_type));
+}
 
-    // NameComparisonResult
-    if (PyType_Ready(&messagerenderer_type) < 0) {
-        return (false);
+MessageRenderer&
+PyMessageRenderer_ToMessageRenderer(PyObject* messagerenderer_obj) {
+    if (messagerenderer_obj == NULL) {
+        isc_throw(PyCPPWrapperException,
+                  "obj argument NULL in MessageRenderer PyObject conversion");
     }
-    Py_INCREF(&messagerenderer_type);
+    s_MessageRenderer* messagerenderer = static_cast<s_MessageRenderer*>(messagerenderer_obj);
+    return (*messagerenderer->cppobj);
+}
 
-    // Class variables
-    // These are added to the tp_dict of the type object
-    addClassVariable(messagerenderer_type, "CASE_INSENSITIVE",
-                     Py_BuildValue("I", MessageRenderer::CASE_INSENSITIVE));
-    addClassVariable(messagerenderer_type, "CASE_SENSITIVE",
-                     Py_BuildValue("I", MessageRenderer::CASE_SENSITIVE));
 
-    PyModule_AddObject(mod, "MessageRenderer",
-                       reinterpret_cast<PyObject*>(&messagerenderer_type));
-    
-    return (true);
-}
 } // namespace python
 } // namespace dns
 } // namespace isc
diff --git a/src/lib/dns/python/messagerenderer_python.h b/src/lib/dns/python/messagerenderer_python.h
index 3bb096e..ea9a940 100644
--- a/src/lib/dns/python/messagerenderer_python.h
+++ b/src/lib/dns/python/messagerenderer_python.h
@@ -17,30 +17,35 @@
 
 #include <Python.h>
 
+#include <util/buffer.h>
+
 namespace isc {
-namespace util {
-class OutputBuffer;
-}
 namespace dns {
 class MessageRenderer;
 
 namespace python {
 
-// The s_* Class simply covers one instantiation of the object.
-//
-// since we don't use *Buffer in the python version (but work with
-// the already existing bytearray type where we use these custom buffers
-// in C++, we need to keep track of one here.
-class s_MessageRenderer : public PyObject {
-public:
-    s_MessageRenderer();
-    isc::util::OutputBuffer* outputbuffer;
-    MessageRenderer* messagerenderer;
-};
-
 extern PyTypeObject messagerenderer_type;
 
-bool initModulePart_MessageRenderer(PyObject* mod);
+/// \brief Checks if the given python object is a MessageRenderer object
+///
+/// \exception PyCPPWrapperException if obj is NULL
+///
+/// \param obj The object to check the type of
+/// \return true if the object is of type MessageRenderer, false otherwise
+bool PyMessageRenderer_Check(PyObject* obj);
+
+/// \brief Returns a reference to the MessageRenderer object contained within the given
+///        Python object.
+///
+/// \note The given object MUST be of type MessageRenderer; this can be checked with
+///       either the right call to ParseTuple("O!"), or with PyMessageRenderer_Check()
+///
+/// \note This is not a copy; if the MessageRenderer is needed when the PyObject
+/// may be destroyed, the caller must copy it itself.
+///
+/// \param messagerenderer_obj The messagerenderer object to convert
+MessageRenderer& PyMessageRenderer_ToMessageRenderer(PyObject* messagerenderer_obj);
 
 } // namespace python
 } // namespace dns
diff --git a/src/lib/dns/python/name_python.cc b/src/lib/dns/python/name_python.cc
index d00c6f7..4043445 100644
--- a/src/lib/dns/python/name_python.cc
+++ b/src/lib/dns/python/name_python.cc
@@ -25,20 +25,25 @@
 #include "messagerenderer_python.h"
 #include "name_python.h"
 
-//
-// Definition of the classes
-//
-
-// For each class, we need a struct, a helper functions (init, destroy,
-// and static wrappers around the methods we export), a list of methods,
-// and a type description
 using namespace isc::dns;
 using namespace isc::dns::python;
 using namespace isc::util;
 using namespace isc::util::python;
 
 namespace {
-// NameComparisonResult
+// The s_* Class simply covers one instantiation of the object.
+class s_NameComparisonResult : public PyObject {
+public:
+    s_NameComparisonResult() : cppobj(NULL) {}
+    NameComparisonResult* cppobj;
+};
+
+class s_Name : public PyObject {
+public:
+    s_Name() : cppobj(NULL), position(0) {}
+    Name* cppobj;
+    size_t position;
+};
 
 int NameComparisonResult_init(s_NameComparisonResult*, PyObject*);
 void NameComparisonResult_destroy(s_NameComparisonResult* self);
@@ -84,9 +89,7 @@ PyObject*
 NameComparisonResult_getRelation(s_NameComparisonResult* self) {
     return (Py_BuildValue("I", self->cppobj->getRelation()));
 }
-// end of NameComparisonResult
 
-// Name
 // Shortcut type which would be convenient for adding class variables safely.
 typedef CPPPyObjectContainer<s_Name, Name> NameContainer;
 
@@ -292,7 +295,7 @@ Name_str(PyObject* self) {
 PyObject*
 Name_toWire(s_Name* self, PyObject* args) {
     PyObject* bytes;
-    s_MessageRenderer* mr;
+    PyObject* mr;
 
     if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) {
         PyObject* bytes_o = bytes;
@@ -306,7 +309,7 @@ Name_toWire(s_Name* self, PyObject* args) {
         Py_DECREF(name_bytes);
         return (result);
     } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
-        self->cppobj->toWire(*mr->messagerenderer);
+        self->cppobj->toWire(PyMessageRenderer_ToMessageRenderer(mr));
         // If we return NULL it is seen as an error, so use this for
         // None returns
         Py_RETURN_NONE;
@@ -495,7 +498,7 @@ Name_isWildCard(s_Name* self) {
         Py_RETURN_FALSE;
     }
 }
-// end of Name
+
 } // end of unnamed namespace
 
 namespace isc {
@@ -634,94 +637,32 @@ PyTypeObject name_type = {
     0                                   // tp_version_tag
 };
 
-// Module Initialization, all statics are initialized here
-bool
-initModulePart_Name(PyObject* mod) {
-    // Add the classes to the module
-    // We initialize the static description object with PyType_Ready(),
-    // then add it to the module
-
-    //
-    // NameComparisonResult
-    //
-    if (PyType_Ready(&name_comparison_result_type) < 0) {
-        return (false);
-    }
-    Py_INCREF(&name_comparison_result_type);
-
-    // Add the enums to the module
-    po_NameRelation = Py_BuildValue("{i:s,i:s,i:s,i:s}",
-                                    NameComparisonResult::SUPERDOMAIN, "SUPERDOMAIN",
-                                    NameComparisonResult::SUBDOMAIN, "SUBDOMAIN",
-                                    NameComparisonResult::EQUAL, "EQUAL",
-                                    NameComparisonResult::COMMONANCESTOR, "COMMONANCESTOR");
-    addClassVariable(name_comparison_result_type, "NameRelation", po_NameRelation);
-
-    PyModule_AddObject(mod, "NameComparisonResult",
-                       reinterpret_cast<PyObject*>(&name_comparison_result_type));
-
-    //
-    // Name
-    //
-    
-    if (PyType_Ready(&name_type) < 0) {
-        return (false);
-    }
-    Py_INCREF(&name_type);
-
-    // Add the constants to the module
-    addClassVariable(name_type, "MAX_WIRE", Py_BuildValue("I", Name::MAX_WIRE));
-    addClassVariable(name_type, "MAX_LABELS", Py_BuildValue("I", Name::MAX_LABELS));
-    addClassVariable(name_type, "MAX_LABELLEN", Py_BuildValue("I", Name::MAX_LABELLEN));
-    addClassVariable(name_type, "MAX_COMPRESS_POINTER", Py_BuildValue("I", Name::MAX_COMPRESS_POINTER));
-    addClassVariable(name_type, "COMPRESS_POINTER_MARK8", Py_BuildValue("I", Name::COMPRESS_POINTER_MARK8));
-    addClassVariable(name_type, "COMPRESS_POINTER_MARK16", Py_BuildValue("I", Name::COMPRESS_POINTER_MARK16));
-
-    s_Name* root_name = PyObject_New(s_Name, &name_type);
-    root_name->cppobj = new Name(Name::ROOT_NAME());
-    PyObject* po_ROOT_NAME = root_name;
-    addClassVariable(name_type, "ROOT_NAME", po_ROOT_NAME);
-
-    PyModule_AddObject(mod, "Name",
-                       reinterpret_cast<PyObject*>(&name_type));
-    
-
-    // Add the exceptions to the module
-    po_EmptyLabel = PyErr_NewException("pydnspp.EmptyLabel", NULL, NULL);
-    PyModule_AddObject(mod, "EmptyLabel", po_EmptyLabel);
-
-    po_TooLongName = PyErr_NewException("pydnspp.TooLongName", NULL, NULL);
-    PyModule_AddObject(mod, "TooLongName", po_TooLongName);
-
-    po_TooLongLabel = PyErr_NewException("pydnspp.TooLongLabel", NULL, NULL);
-    PyModule_AddObject(mod, "TooLongLabel", po_TooLongLabel);
-
-    po_BadLabelType = PyErr_NewException("pydnspp.BadLabelType", NULL, NULL);
-    PyModule_AddObject(mod, "BadLabelType", po_BadLabelType);
-
-    po_BadEscape = PyErr_NewException("pydnspp.BadEscape", NULL, NULL);
-    PyModule_AddObject(mod, "BadEscape", po_BadEscape);
-
-    po_IncompleteName = PyErr_NewException("pydnspp.IncompleteName", NULL, NULL);
-    PyModule_AddObject(mod, "IncompleteName", po_IncompleteName);
-
-    po_InvalidBufferPosition = PyErr_NewException("pydnspp.InvalidBufferPosition", NULL, NULL);
-    PyModule_AddObject(mod, "InvalidBufferPosition", po_InvalidBufferPosition);
-
-    // This one could have gone into the message_python.cc file, but is
-    // already needed here.
-    po_DNSMessageFORMERR = PyErr_NewException("pydnspp.DNSMessageFORMERR", NULL, NULL);
-    PyModule_AddObject(mod, "DNSMessageFORMERR", po_DNSMessageFORMERR);
-
-    return (true);
-}
-
 PyObject*
 createNameObject(const Name& source) {
-    NameContainer container = PyObject_New(s_Name, &name_type);
+    NameContainer container(PyObject_New(s_Name, &name_type));
     container.set(new Name(source));
     return (container.release());
 }
+
+bool
+PyName_Check(PyObject* obj) {
+    if (obj == NULL) {
+        isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck");
+    }
+    return (PyObject_TypeCheck(obj, &name_type));
+}
+
+const Name&
+PyName_ToName(const PyObject* name_obj) {
+    if (name_obj == NULL) {
+        isc_throw(PyCPPWrapperException,
+                  "obj argument NULL in Name PyObject conversion");
+    }
+    const s_Name* name = static_cast<const s_Name*>(name_obj);
+    return (*name->cppobj);
+}
+
+
 } // namespace python
 } // namespace dns
 } // namespace isc
diff --git a/src/lib/dns/python/name_python.h b/src/lib/dns/python/name_python.h
index f8e793d..86d7fd0 100644
--- a/src/lib/dns/python/name_python.h
+++ b/src/lib/dns/python/name_python.h
@@ -17,20 +17,12 @@
 
 #include <Python.h>
 
-#include <util/python/pycppwrapper_util.h>
-
 namespace isc {
 namespace dns {
-class NameComparisonResult;
 class Name;
 
 namespace python {
 
-//
-// Declaration of the custom exceptions
-// Initialization and addition of these go in the module init at the
-// end
-//
 extern PyObject* po_EmptyLabel;
 extern PyObject* po_TooLongName;
 extern PyObject* po_TooLongLabel;
@@ -47,25 +39,9 @@ extern PyObject* po_DNSMessageFORMERR;
 //
 extern PyObject* po_NameRelation;
 
-// The s_* Class simply covers one instantiation of the object.
-class s_NameComparisonResult : public PyObject {
-public:
-    s_NameComparisonResult() : cppobj(NULL) {}
-    NameComparisonResult* cppobj;
-};
-
-class s_Name : public PyObject {
-public:
-    s_Name() : cppobj(NULL), position(0) {}
-    Name* cppobj;
-    size_t position;
-};
-
 extern PyTypeObject name_comparison_result_type;
 extern PyTypeObject name_type;
 
-bool initModulePart_Name(PyObject* mod);
-
 /// This is A simple shortcut to create a python Name object (in the
 /// form of a pointer to PyObject) with minimal exception safety.
 /// On success, it returns a valid pointer to PyObject with a reference
@@ -74,6 +50,27 @@ bool initModulePart_Name(PyObject* mod);
 /// This function is expected to be called with in a try block
 /// followed by necessary setup for python exception.
 PyObject* createNameObject(const Name& source);
+
+/// \brief Checks if the given python object is a Name object
+///
+/// \exception PyCPPWrapperException if obj is NULL
+///
+/// \param obj The object to check the type of
+/// \return true if the object is of type Name, false otherwise
+bool PyName_Check(PyObject* obj);
+
+/// \brief Returns a reference to the Name object contained within the given
+///        Python object.
+///
+/// \note The given object MUST be of type Name; this can be checked with
+///       either the right call to ParseTuple("O!"), or with PyName_Check()
+///
+/// \note This is not a copy; if the Name is needed when the PyObject
+/// may be destroyed, the caller must copy it itself.
+///
+/// \param name_obj The name object to convert
+const Name& PyName_ToName(const PyObject* name_obj);
+
 } // namespace python
 } // namespace dns
 } // namespace isc
diff --git a/src/lib/dns/python/opcode_python.cc b/src/lib/dns/python/opcode_python.cc
index 0e2a30b..50436a9 100644
--- a/src/lib/dns/python/opcode_python.cc
+++ b/src/lib/dns/python/opcode_python.cc
@@ -12,32 +12,31 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <dns/opcode.h>
-
-using namespace isc::dns;
+#include <Python.h>
 
-//
-// Declaration of the custom exceptions (None for this class)
+#include <dns/opcode.h>
+#include <util/python/pycppwrapper_util.h>
 
-//
-// Definition of the classes
-//
+#include "pydnspp_common.h"
+#include "opcode_python.h"
+#include "edns_python.h"
 
-// For each class, we need a struct, a helper functions (init, destroy,
-// and static wrappers around the methods we export), a list of methods,
-// and a type description
+using namespace isc::dns;
+using namespace isc::dns::python;
+using namespace isc::util;
+using namespace isc::util::python;
 
 namespace {
-//
-// Opcode
-//
+
 class s_Opcode : public PyObject {
 public:
-    s_Opcode() : opcode(NULL), static_code(false) {}
-    const Opcode* opcode;
+    s_Opcode() : cppobj(NULL), static_code(false) {}
+    const isc::dns::Opcode* cppobj;
     bool static_code;
 };
 
+typedef CPPPyObjectContainer<s_Opcode, Opcode> OpcodeContainer;
+
 int Opcode_init(s_Opcode* const self, PyObject* args);
 void Opcode_destroy(s_Opcode* const self);
 
@@ -103,64 +102,13 @@ PyMethodDef Opcode_methods[] = {
     { NULL, NULL, 0, NULL }
 };
 
-PyTypeObject opcode_type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "pydnspp.Opcode",
-    sizeof(s_Opcode),                   // tp_basicsize
-    0,                                  // tp_itemsize
-    (destructor)Opcode_destroy,         // tp_dealloc
-    NULL,                               // tp_print
-    NULL,                               // tp_getattr
-    NULL,                               // tp_setattr
-    NULL,                               // tp_reserved
-    NULL,                               // tp_repr
-    NULL,                               // tp_as_number
-    NULL,                               // tp_as_sequence
-    NULL,                               // tp_as_mapping
-    NULL,                               // tp_hash 
-    NULL,                               // tp_call
-    Opcode_str,                         // tp_str
-    NULL,                               // tp_getattro
-    NULL,                               // tp_setattro
-    NULL,                               // tp_as_buffer
-    Py_TPFLAGS_DEFAULT,                 // tp_flags
-    "The Opcode class objects represent standard OPCODEs "
-    "of the header section of DNS messages.",
-    NULL,                               // tp_traverse
-    NULL,                               // tp_clear
-    (richcmpfunc)Opcode_richcmp,        // tp_richcompare
-    0,                                  // tp_weaklistoffset
-    NULL,                               // tp_iter
-    NULL,                               // tp_iternext
-    Opcode_methods,                     // tp_methods
-    NULL,                               // tp_members
-    NULL,                               // tp_getset
-    NULL,                               // tp_base
-    NULL,                               // tp_dict
-    NULL,                               // tp_descr_get
-    NULL,                               // tp_descr_set
-    0,                                  // tp_dictoffset
-    (initproc)Opcode_init,              // tp_init
-    NULL,                               // tp_alloc
-    PyType_GenericNew,                  // tp_new
-    NULL,                               // tp_free
-    NULL,                               // tp_is_gc
-    NULL,                               // tp_bases
-    NULL,                               // tp_mro
-    NULL,                               // tp_cache
-    NULL,                               // tp_subclasses
-    NULL,                               // tp_weaklist
-    NULL,                               // tp_del
-    0                                   // tp_version_tag
-};
-
 
 int
 Opcode_init(s_Opcode* const self, PyObject* args) {
     uint8_t code = 0;
     if (PyArg_ParseTuple(args, "b", &code)) {
         try {
-            self->opcode = new Opcode(code);
+            self->cppobj = new Opcode(code);
             self->static_code = false;
         } catch (const isc::OutOfRange& ex) {
             PyErr_SetString(PyExc_OverflowError, ex.what());
@@ -181,22 +129,22 @@ Opcode_init(s_Opcode* const self, PyObject* args) {
 void
 Opcode_destroy(s_Opcode* const self) {
     // Depending on whether we created the rcode or are referring
-    // to a global static one, we do or do not delete self->opcode here
+    // to a global static one, we do or do not delete self->cppobj here
     if (!self->static_code) {
-        delete self->opcode;
+        delete self->cppobj;
     }
-    self->opcode = NULL;
+    self->cppobj = NULL;
     Py_TYPE(self)->tp_free(self);
 }
 
 PyObject*
 Opcode_getCode(const s_Opcode* const self) {
-    return (Py_BuildValue("I", self->opcode->getCode()));
+    return (Py_BuildValue("I", self->cppobj->getCode()));
 }
 
 PyObject*
 Opcode_toText(const s_Opcode* const self) {
-    return (Py_BuildValue("s", self->opcode->toText().c_str()));
+    return (Py_BuildValue("s", self->cppobj->toText().c_str()));
 }
 
 PyObject*
@@ -211,7 +159,7 @@ PyObject*
 Opcode_createStatic(const Opcode& opcode) {
     s_Opcode* ret = PyObject_New(s_Opcode, &opcode_type);
     if (ret != NULL) {
-        ret->opcode = &opcode;
+        ret->cppobj = &opcode;
         ret->static_code = true;
     }
     return (ret);
@@ -297,7 +245,7 @@ Opcode_RESERVED15(const s_Opcode*) {
     return (Opcode_createStatic(Opcode::RESERVED15()));
 }
 
-PyObject* 
+PyObject*
 Opcode_richcmp(const s_Opcode* const self, const s_Opcode* const other,
                const int op)
 {
@@ -318,10 +266,10 @@ Opcode_richcmp(const s_Opcode* const self, const s_Opcode* const other,
         PyErr_SetString(PyExc_TypeError, "Unorderable type; Opcode");
         return (NULL);
     case Py_EQ:
-        c = (*self->opcode == *other->opcode);
+        c = (*self->cppobj == *other->cppobj);
         break;
     case Py_NE:
-        c = (*self->opcode != *other->opcode);
+        c = (*self->cppobj != *other->cppobj);
         break;
     case Py_GT:
         PyErr_SetString(PyExc_TypeError, "Unorderable type; Opcode");
@@ -336,55 +284,88 @@ Opcode_richcmp(const s_Opcode* const self, const s_Opcode* const other,
         Py_RETURN_FALSE;
 }
 
-// Module Initialization, all statics are initialized here
+} // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+
+PyTypeObject opcode_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "pydnspp.Opcode",
+    sizeof(s_Opcode),                   // tp_basicsize
+    0,                                  // tp_itemsize
+    (destructor)Opcode_destroy,         // tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash
+    NULL,                               // tp_call
+    Opcode_str,                         // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    "The Opcode class objects represent standard OPCODEs "
+    "of the header section of DNS messages.",
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    (richcmpfunc)Opcode_richcmp,        // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    Opcode_methods,                     // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    (initproc)Opcode_init,              // tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
+
+PyObject*
+createOpcodeObject(const Opcode& source) {
+    OpcodeContainer container(PyObject_New(s_Opcode, &opcode_type));
+    container.set(new Opcode(source));
+    return (container.release());
+}
+
 bool
-initModulePart_Opcode(PyObject* mod) {
-    // We initialize the static description object with PyType_Ready(),
-    // then add it to the module. This is not just a check! (leaving
-    // this out results in segmentation faults)
-    if (PyType_Ready(&opcode_type) < 0) {
-        return (false);
-    }
-    Py_INCREF(&opcode_type);
-    void* p = &opcode_type;
-    if (PyModule_AddObject(mod, "Opcode", static_cast<PyObject*>(p)) != 0) {
-        Py_DECREF(&opcode_type);
-        return (false);
+PyOpcode_Check(PyObject* obj) {
+    if (obj == NULL) {
+        isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck");
     }
+    return (PyObject_TypeCheck(obj, &opcode_type));
+}
 
-    addClassVariable(opcode_type, "QUERY_CODE",
-                     Py_BuildValue("h", Opcode::QUERY_CODE));
-    addClassVariable(opcode_type, "IQUERY_CODE",
-                     Py_BuildValue("h", Opcode::IQUERY_CODE));
-    addClassVariable(opcode_type, "STATUS_CODE",
-                     Py_BuildValue("h", Opcode::STATUS_CODE));
-    addClassVariable(opcode_type, "RESERVED3_CODE",
-                     Py_BuildValue("h", Opcode::RESERVED3_CODE));
-    addClassVariable(opcode_type, "NOTIFY_CODE",
-                     Py_BuildValue("h", Opcode::NOTIFY_CODE));
-    addClassVariable(opcode_type, "UPDATE_CODE",
-                     Py_BuildValue("h", Opcode::UPDATE_CODE));
-    addClassVariable(opcode_type, "RESERVED6_CODE",
-                     Py_BuildValue("h", Opcode::RESERVED6_CODE));
-    addClassVariable(opcode_type, "RESERVED7_CODE",
-                     Py_BuildValue("h", Opcode::RESERVED7_CODE));
-    addClassVariable(opcode_type, "RESERVED8_CODE",
-                     Py_BuildValue("h", Opcode::RESERVED8_CODE));
-    addClassVariable(opcode_type, "RESERVED9_CODE",
-                     Py_BuildValue("h", Opcode::RESERVED9_CODE));
-    addClassVariable(opcode_type, "RESERVED10_CODE",
-                     Py_BuildValue("h", Opcode::RESERVED10_CODE));
-    addClassVariable(opcode_type, "RESERVED11_CODE",
-                     Py_BuildValue("h", Opcode::RESERVED11_CODE));
-    addClassVariable(opcode_type, "RESERVED12_CODE",
-                     Py_BuildValue("h", Opcode::RESERVED12_CODE));
-    addClassVariable(opcode_type, "RESERVED13_CODE",
-                     Py_BuildValue("h", Opcode::RESERVED13_CODE));
-    addClassVariable(opcode_type, "RESERVED14_CODE",
-                     Py_BuildValue("h", Opcode::RESERVED14_CODE));
-    addClassVariable(opcode_type, "RESERVED15_CODE",
-                     Py_BuildValue("h", Opcode::RESERVED15_CODE));
-
-    return (true);
+const Opcode&
+PyOpcode_ToOpcode(const PyObject* opcode_obj) {
+    if (opcode_obj == NULL) {
+        isc_throw(PyCPPWrapperException,
+                  "obj argument NULL in Opcode PyObject conversion");
+    }
+    const s_Opcode* opcode = static_cast<const s_Opcode*>(opcode_obj);
+    return (*opcode->cppobj);
 }
-} // end of unnamed namespace
+
+} // end python namespace
+} // end dns namespace
+} // end isc namespace
diff --git a/src/lib/dns/python/opcode_python.h b/src/lib/dns/python/opcode_python.h
new file mode 100644
index 0000000..d0aec15
--- /dev/null
+++ b/src/lib/dns/python/opcode_python.h
@@ -0,0 +1,64 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_OPCODE_H
+#define __PYTHON_OPCODE_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace dns {
+class Opcode;
+
+namespace python {
+
+extern PyTypeObject opcode_type;
+
+/// This is a simple shortcut to create a python Opcode object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called within a try block
+/// followed by necessary setup for python exception.
+PyObject* createOpcodeObject(const Opcode& source);
+
+/// \brief Checks if the given python object is a Opcode object
+///
+/// \exception PyCPPWrapperException if obj is NULL
+///
+/// \param obj The object to check the type of
+/// \return true if the object is of type Opcode, false otherwise
+bool PyOpcode_Check(PyObject* obj);
+
+/// \brief Returns a reference to the Opcode object contained within the given
+///        Python object.
+///
+/// \note The given object MUST be of type Opcode; this can be checked with
+///       either the right call to ParseTuple("O!"), or with PyOpcode_Check()
+///
+/// \note This is not a copy; if the Opcode is needed when the PyObject
+/// may be destroyed, the caller must copy it itself.
+///
+/// \param opcode_obj The opcode object to convert
+const Opcode& PyOpcode_ToOpcode(const PyObject* opcode_obj);
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_OPCODE_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/pydnspp.cc b/src/lib/dns/python/pydnspp.cc
index 07abf71..830876c 100644
--- a/src/lib/dns/python/pydnspp.cc
+++ b/src/lib/dns/python/pydnspp.cc
@@ -21,63 +21,680 @@
 // name initModulePart_<name>, and return true/false instead of
 // NULL/*mod
 //
-// And of course care has to be taken that all identifiers be unique
+// The big init function is split up into a separate initModulePart function
+// for each class we add.
 
 #define PY_SSIZE_T_CLEAN
 #include <Python.h>
 #include <structmember.h>
 
-#include <config.h>
-
-#include <exceptions/exceptions.h>
-
-#include <util/buffer.h>
-
-#include <dns/exceptions.h>
-#include <dns/name.h>
-#include <dns/messagerenderer.h>
+#include <dns/message.h>
+#include <dns/opcode.h>
+#include <dns/tsig.h>
+#include <util/python/pycppwrapper_util.h>
 
 #include "pydnspp_common.h"
+
+#include "edns_python.h"
+#include "message_python.h"
 #include "messagerenderer_python.h"
 #include "name_python.h"
+#include "opcode_python.h"
+#include "pydnspp_common.h"
+#include "pydnspp_towire.h"
+#include "question_python.h"
 #include "rcode_python.h"
+#include "rdata_python.h"
+#include "rrclass_python.h"
+#include "rrset_python.h"
+#include "rrttl_python.h"
+#include "rrtype_python.h"
+#include "tsigerror_python.h"
 #include "tsigkey_python.h"
+#include "tsig_python.h"
 #include "tsig_rdata_python.h"
-#include "tsigerror_python.h"
 #include "tsigrecord_python.h"
-#include "tsig_python.h"
 
-namespace isc {
-namespace dns {
-namespace python {
-// For our 'general' isc::Exceptions
-PyObject* po_IscException;
-PyObject* po_InvalidParameter;
+using namespace isc::dns;
+using namespace isc::dns::python;
+using namespace isc::util::python;
+
+namespace {
+
+bool
+initModulePart_EDNS(PyObject* mod) {
+    // We initialize the static description object with PyType_Ready(),
+    // then add it to the module. This is not just a check! (leaving
+    // this out results in segmentation faults)
+    //
+    // After the type has been initialized, we initialize any exceptions
+    // that are defined in the wrapper for this class, and add constants
+    // to the type, if any
+
+    if (PyType_Ready(&edns_type) < 0) {
+        return (false);
+    }
+    Py_INCREF(&edns_type);
+    void* p = &edns_type;
+    PyModule_AddObject(mod, "EDNS", static_cast<PyObject*>(p));
+
+    addClassVariable(edns_type, "SUPPORTED_VERSION",
+                     Py_BuildValue("B", EDNS::SUPPORTED_VERSION));
 
-// For our own isc::dns::Exception
-PyObject* po_DNSMessageBADVERS;
+    return (true);
 }
+
+bool
+initModulePart_Message(PyObject* mod) {
+    if (PyType_Ready(&message_type) < 0) {
+        return (false);
+    }
+    Py_INCREF(&message_type);
+
+    // Class variables
+    // These are added to the tp_dict of the type object
+    //
+    addClassVariable(message_type, "PARSE",
+                     Py_BuildValue("I", Message::PARSE));
+    addClassVariable(message_type, "RENDER",
+                     Py_BuildValue("I", Message::RENDER));
+
+    addClassVariable(message_type, "HEADERFLAG_QR",
+                     Py_BuildValue("I", Message::HEADERFLAG_QR));
+    addClassVariable(message_type, "HEADERFLAG_AA",
+                     Py_BuildValue("I", Message::HEADERFLAG_AA));
+    addClassVariable(message_type, "HEADERFLAG_TC",
+                     Py_BuildValue("I", Message::HEADERFLAG_TC));
+    addClassVariable(message_type, "HEADERFLAG_RD",
+                     Py_BuildValue("I", Message::HEADERFLAG_RD));
+    addClassVariable(message_type, "HEADERFLAG_RA",
+                     Py_BuildValue("I", Message::HEADERFLAG_RA));
+    addClassVariable(message_type, "HEADERFLAG_AD",
+                     Py_BuildValue("I", Message::HEADERFLAG_AD));
+    addClassVariable(message_type, "HEADERFLAG_CD",
+                     Py_BuildValue("I", Message::HEADERFLAG_CD));
+
+    addClassVariable(message_type, "SECTION_QUESTION",
+                     Py_BuildValue("I", Message::SECTION_QUESTION));
+    addClassVariable(message_type, "SECTION_ANSWER",
+                     Py_BuildValue("I", Message::SECTION_ANSWER));
+    addClassVariable(message_type, "SECTION_AUTHORITY",
+                     Py_BuildValue("I", Message::SECTION_AUTHORITY));
+    addClassVariable(message_type, "SECTION_ADDITIONAL",
+                     Py_BuildValue("I", Message::SECTION_ADDITIONAL));
+
+    addClassVariable(message_type, "DEFAULT_MAX_UDPSIZE",
+                     Py_BuildValue("I", Message::DEFAULT_MAX_UDPSIZE));
+
+    /* Class-specific exceptions */
+    po_MessageTooShort = PyErr_NewException("pydnspp.MessageTooShort", NULL,
+                                            NULL);
+    PyModule_AddObject(mod, "MessageTooShort", po_MessageTooShort);
+    po_InvalidMessageSection =
+        PyErr_NewException("pydnspp.InvalidMessageSection", NULL, NULL);
+    PyModule_AddObject(mod, "InvalidMessageSection", po_InvalidMessageSection);
+    po_InvalidMessageOperation =
+        PyErr_NewException("pydnspp.InvalidMessageOperation", NULL, NULL);
+    PyModule_AddObject(mod, "InvalidMessageOperation",
+                       po_InvalidMessageOperation);
+    po_InvalidMessageUDPSize =
+        PyErr_NewException("pydnspp.InvalidMessageUDPSize", NULL, NULL);
+    PyModule_AddObject(mod, "InvalidMessageUDPSize", po_InvalidMessageUDPSize);
+    po_DNSMessageBADVERS = PyErr_NewException("pydnspp.DNSMessageBADVERS",
+                                              NULL, NULL);
+    PyModule_AddObject(mod, "DNSMessageBADVERS", po_DNSMessageBADVERS);
+
+    PyModule_AddObject(mod, "Message",
+                       reinterpret_cast<PyObject*>(&message_type));
+
+
+    return (true);
 }
+
+bool
+initModulePart_MessageRenderer(PyObject* mod) {
+    if (PyType_Ready(&messagerenderer_type) < 0) {
+        return (false);
+    }
+    Py_INCREF(&messagerenderer_type);
+
+    addClassVariable(messagerenderer_type, "CASE_INSENSITIVE",
+                     Py_BuildValue("I", MessageRenderer::CASE_INSENSITIVE));
+    addClassVariable(messagerenderer_type, "CASE_SENSITIVE",
+                     Py_BuildValue("I", MessageRenderer::CASE_SENSITIVE));
+
+    PyModule_AddObject(mod, "MessageRenderer",
+                       reinterpret_cast<PyObject*>(&messagerenderer_type));
+
+    return (true);
 }
 
-// order is important here!
-using namespace isc::dns::python;
+bool
+initModulePart_Name(PyObject* mod) {
 
-#include <dns/python/rrclass_python.cc>        // needs Messagerenderer
-#include <dns/python/rrtype_python.cc>         // needs Messagerenderer
-#include <dns/python/rrttl_python.cc>          // needs Messagerenderer
-#include <dns/python/rdata_python.cc>          // needs Type, Class
-#include <dns/python/rrset_python.cc>          // needs Rdata, RRTTL
-#include <dns/python/question_python.cc>       // needs RRClass, RRType, RRTTL,
-                                               // Name
-#include <dns/python/opcode_python.cc>
-#include <dns/python/edns_python.cc>           // needs Messagerenderer, Rcode
-#include <dns/python/message_python.cc>        // needs RRset, Question
+    //
+    // NameComparisonResult
+    //
+    if (PyType_Ready(&name_comparison_result_type) < 0) {
+        return (false);
+    }
+    Py_INCREF(&name_comparison_result_type);
+
+    // Add the enums to the module
+    po_NameRelation = Py_BuildValue("{i:s,i:s,i:s,i:s}",
+        NameComparisonResult::SUPERDOMAIN, "SUPERDOMAIN",
+        NameComparisonResult::SUBDOMAIN, "SUBDOMAIN",
+        NameComparisonResult::EQUAL, "EQUAL",
+        NameComparisonResult::COMMONANCESTOR, "COMMONANCESTOR");
+    addClassVariable(name_comparison_result_type, "NameRelation",
+                     po_NameRelation);
+
+    PyModule_AddObject(mod, "NameComparisonResult",
+        reinterpret_cast<PyObject*>(&name_comparison_result_type));
+
+    //
+    // Name
+    //
+
+    if (PyType_Ready(&name_type) < 0) {
+        return (false);
+    }
+    Py_INCREF(&name_type);
+
+    // Add the constants to the module
+    addClassVariable(name_type, "MAX_WIRE",
+                     Py_BuildValue("I", Name::MAX_WIRE));
+    addClassVariable(name_type, "MAX_LABELS",
+                     Py_BuildValue("I", Name::MAX_LABELS));
+    addClassVariable(name_type, "MAX_LABELLEN",
+                     Py_BuildValue("I", Name::MAX_LABELLEN));
+    addClassVariable(name_type, "MAX_COMPRESS_POINTER",
+                     Py_BuildValue("I", Name::MAX_COMPRESS_POINTER));
+    addClassVariable(name_type, "COMPRESS_POINTER_MARK8",
+                     Py_BuildValue("I", Name::COMPRESS_POINTER_MARK8));
+    addClassVariable(name_type, "COMPRESS_POINTER_MARK16",
+                     Py_BuildValue("I", Name::COMPRESS_POINTER_MARK16));
+
+    addClassVariable(name_type, "ROOT_NAME",
+                     createNameObject(Name::ROOT_NAME()));
+
+    PyModule_AddObject(mod, "Name",
+                       reinterpret_cast<PyObject*>(&name_type));
+
+
+    // Add the exceptions to the module
+    po_EmptyLabel = PyErr_NewException("pydnspp.EmptyLabel", NULL, NULL);
+    PyModule_AddObject(mod, "EmptyLabel", po_EmptyLabel);
+
+    po_TooLongName = PyErr_NewException("pydnspp.TooLongName", NULL, NULL);
+    PyModule_AddObject(mod, "TooLongName", po_TooLongName);
+
+    po_TooLongLabel = PyErr_NewException("pydnspp.TooLongLabel", NULL, NULL);
+    PyModule_AddObject(mod, "TooLongLabel", po_TooLongLabel);
+
+    po_BadLabelType = PyErr_NewException("pydnspp.BadLabelType", NULL, NULL);
+    PyModule_AddObject(mod, "BadLabelType", po_BadLabelType);
+
+    po_BadEscape = PyErr_NewException("pydnspp.BadEscape", NULL, NULL);
+    PyModule_AddObject(mod, "BadEscape", po_BadEscape);
+
+    po_IncompleteName = PyErr_NewException("pydnspp.IncompleteName", NULL, NULL);
+    PyModule_AddObject(mod, "IncompleteName", po_IncompleteName);
+
+    po_InvalidBufferPosition =
+        PyErr_NewException("pydnspp.InvalidBufferPosition", NULL, NULL);
+    PyModule_AddObject(mod, "InvalidBufferPosition", po_InvalidBufferPosition);
+
+    // This one could have gone into the message_python.cc file, but is
+    // already needed here.
+    po_DNSMessageFORMERR = PyErr_NewException("pydnspp.DNSMessageFORMERR",
+                                              NULL, NULL);
+    PyModule_AddObject(mod, "DNSMessageFORMERR", po_DNSMessageFORMERR);
+
+    return (true);
+}
+
+bool
+initModulePart_Opcode(PyObject* mod) {
+    if (PyType_Ready(&opcode_type) < 0) {
+        return (false);
+    }
+    Py_INCREF(&opcode_type);
+    void* p = &opcode_type;
+    if (PyModule_AddObject(mod, "Opcode", static_cast<PyObject*>(p)) != 0) {
+        Py_DECREF(&opcode_type);
+        return (false);
+    }
+
+    addClassVariable(opcode_type, "QUERY_CODE",
+                     Py_BuildValue("h", Opcode::QUERY_CODE));
+    addClassVariable(opcode_type, "IQUERY_CODE",
+                     Py_BuildValue("h", Opcode::IQUERY_CODE));
+    addClassVariable(opcode_type, "STATUS_CODE",
+                     Py_BuildValue("h", Opcode::STATUS_CODE));
+    addClassVariable(opcode_type, "RESERVED3_CODE",
+                     Py_BuildValue("h", Opcode::RESERVED3_CODE));
+    addClassVariable(opcode_type, "NOTIFY_CODE",
+                     Py_BuildValue("h", Opcode::NOTIFY_CODE));
+    addClassVariable(opcode_type, "UPDATE_CODE",
+                     Py_BuildValue("h", Opcode::UPDATE_CODE));
+    addClassVariable(opcode_type, "RESERVED6_CODE",
+                     Py_BuildValue("h", Opcode::RESERVED6_CODE));
+    addClassVariable(opcode_type, "RESERVED7_CODE",
+                     Py_BuildValue("h", Opcode::RESERVED7_CODE));
+    addClassVariable(opcode_type, "RESERVED8_CODE",
+                     Py_BuildValue("h", Opcode::RESERVED8_CODE));
+    addClassVariable(opcode_type, "RESERVED9_CODE",
+                     Py_BuildValue("h", Opcode::RESERVED9_CODE));
+    addClassVariable(opcode_type, "RESERVED10_CODE",
+                     Py_BuildValue("h", Opcode::RESERVED10_CODE));
+    addClassVariable(opcode_type, "RESERVED11_CODE",
+                     Py_BuildValue("h", Opcode::RESERVED11_CODE));
+    addClassVariable(opcode_type, "RESERVED12_CODE",
+                     Py_BuildValue("h", Opcode::RESERVED12_CODE));
+    addClassVariable(opcode_type, "RESERVED13_CODE",
+                     Py_BuildValue("h", Opcode::RESERVED13_CODE));
+    addClassVariable(opcode_type, "RESERVED14_CODE",
+                     Py_BuildValue("h", Opcode::RESERVED14_CODE));
+    addClassVariable(opcode_type, "RESERVED15_CODE",
+                     Py_BuildValue("h", Opcode::RESERVED15_CODE));
+
+    return (true);
+}
+
+bool
+initModulePart_Question(PyObject* mod) {
+    if (PyType_Ready(&question_type) < 0) {
+        return (false);
+    }
+    Py_INCREF(&question_type);
+    PyModule_AddObject(mod, "Question",
+                       reinterpret_cast<PyObject*>(&question_type));
+
+    return (true);
+}
+
+bool
+initModulePart_Rcode(PyObject* mod) {
+    if (PyType_Ready(&rcode_type) < 0) {
+        return (false);
+    }
+    Py_INCREF(&rcode_type);
+    void* p = &rcode_type;
+    if (PyModule_AddObject(mod, "Rcode", static_cast<PyObject*>(p)) != 0) {
+        Py_DECREF(&rcode_type);
+        return (false);
+    }
+
+    addClassVariable(rcode_type, "NOERROR_CODE",
+                     Py_BuildValue("h", Rcode::NOERROR_CODE));
+    addClassVariable(rcode_type, "FORMERR_CODE",
+                     Py_BuildValue("h", Rcode::FORMERR_CODE));
+    addClassVariable(rcode_type, "SERVFAIL_CODE",
+                     Py_BuildValue("h", Rcode::SERVFAIL_CODE));
+    addClassVariable(rcode_type, "NXDOMAIN_CODE",
+                     Py_BuildValue("h", Rcode::NXDOMAIN_CODE));
+    addClassVariable(rcode_type, "NOTIMP_CODE",
+                     Py_BuildValue("h", Rcode::NOTIMP_CODE));
+    addClassVariable(rcode_type, "REFUSED_CODE",
+                     Py_BuildValue("h", Rcode::REFUSED_CODE));
+    addClassVariable(rcode_type, "YXDOMAIN_CODE",
+                     Py_BuildValue("h", Rcode::YXDOMAIN_CODE));
+    addClassVariable(rcode_type, "YXRRSET_CODE",
+                     Py_BuildValue("h", Rcode::YXRRSET_CODE));
+    addClassVariable(rcode_type, "NXRRSET_CODE",
+                     Py_BuildValue("h", Rcode::NXRRSET_CODE));
+    addClassVariable(rcode_type, "NOTAUTH_CODE",
+                     Py_BuildValue("h", Rcode::NOTAUTH_CODE));
+    addClassVariable(rcode_type, "NOTZONE_CODE",
+                     Py_BuildValue("h", Rcode::NOTZONE_CODE));
+    addClassVariable(rcode_type, "RESERVED11_CODE",
+                     Py_BuildValue("h", Rcode::RESERVED11_CODE));
+    addClassVariable(rcode_type, "RESERVED12_CODE",
+                     Py_BuildValue("h", Rcode::RESERVED12_CODE));
+    addClassVariable(rcode_type, "RESERVED13_CODE",
+                     Py_BuildValue("h", Rcode::RESERVED13_CODE));
+    addClassVariable(rcode_type, "RESERVED14_CODE",
+                     Py_BuildValue("h", Rcode::RESERVED14_CODE));
+    addClassVariable(rcode_type, "RESERVED15_CODE",
+                     Py_BuildValue("h", Rcode::RESERVED15_CODE));
+    addClassVariable(rcode_type, "BADVERS_CODE",
+                     Py_BuildValue("h", Rcode::BADVERS_CODE));
+
+    return (true);
+}
+
+bool
+initModulePart_Rdata(PyObject* mod) {
+    if (PyType_Ready(&rdata_type) < 0) {
+        return (false);
+    }
+    Py_INCREF(&rdata_type);
+    PyModule_AddObject(mod, "Rdata",
+                       reinterpret_cast<PyObject*>(&rdata_type));
+
+    // Add the exceptions to the class
+    po_InvalidRdataLength = PyErr_NewException("pydnspp.InvalidRdataLength",
+                                               NULL, NULL);
+    PyModule_AddObject(mod, "InvalidRdataLength", po_InvalidRdataLength);
+
+    po_InvalidRdataText = PyErr_NewException("pydnspp.InvalidRdataText",
+                                             NULL, NULL);
+    PyModule_AddObject(mod, "InvalidRdataText", po_InvalidRdataText);
+
+    po_CharStringTooLong = PyErr_NewException("pydnspp.CharStringTooLong",
+                                              NULL, NULL);
+    PyModule_AddObject(mod, "CharStringTooLong", po_CharStringTooLong);
+
+
+    return (true);
+}
+
+bool
+initModulePart_RRClass(PyObject* mod) {
+    po_InvalidRRClass = PyErr_NewException("pydnspp.InvalidRRClass",
+                                           NULL, NULL);
+    Py_INCREF(po_InvalidRRClass);
+    PyModule_AddObject(mod, "InvalidRRClass", po_InvalidRRClass);
+    po_IncompleteRRClass = PyErr_NewException("pydnspp.IncompleteRRClass",
+                                              NULL, NULL);
+    Py_INCREF(po_IncompleteRRClass);
+    PyModule_AddObject(mod, "IncompleteRRClass", po_IncompleteRRClass);
+
+    if (PyType_Ready(&rrclass_type) < 0) {
+        return (false);
+    }
+    Py_INCREF(&rrclass_type);
+    PyModule_AddObject(mod, "RRClass",
+                       reinterpret_cast<PyObject*>(&rrclass_type));
+
+    return (true);
+}
+
+bool
+initModulePart_RRset(PyObject* mod) {
+    po_EmptyRRset = PyErr_NewException("pydnspp.EmptyRRset", NULL, NULL);
+    PyModule_AddObject(mod, "EmptyRRset", po_EmptyRRset);
+
+    // NameComparisonResult
+    if (PyType_Ready(&rrset_type) < 0) {
+        return (false);
+    }
+    Py_INCREF(&rrset_type);
+    PyModule_AddObject(mod, "RRset",
+                       reinterpret_cast<PyObject*>(&rrset_type));
+
+    return (true);
+}
+
+bool
+initModulePart_RRTTL(PyObject* mod) {
+    po_InvalidRRTTL = PyErr_NewException("pydnspp.InvalidRRTTL", NULL, NULL);
+    PyModule_AddObject(mod, "InvalidRRTTL", po_InvalidRRTTL);
+    po_IncompleteRRTTL = PyErr_NewException("pydnspp.IncompleteRRTTL",
+                                            NULL, NULL);
+    PyModule_AddObject(mod, "IncompleteRRTTL", po_IncompleteRRTTL);
+
+    if (PyType_Ready(&rrttl_type) < 0) {
+        return (false);
+    }
+    Py_INCREF(&rrttl_type);
+    PyModule_AddObject(mod, "RRTTL",
+                       reinterpret_cast<PyObject*>(&rrttl_type));
+
+    return (true);
+}
+
+bool
+initModulePart_RRType(PyObject* mod) {
+    // Add the exceptions to the module
+    po_InvalidRRType = PyErr_NewException("pydnspp.InvalidRRType", NULL, NULL);
+    PyModule_AddObject(mod, "InvalidRRType", po_InvalidRRType);
+    po_IncompleteRRType = PyErr_NewException("pydnspp.IncompleteRRType",
+                                             NULL, NULL);
+    PyModule_AddObject(mod, "IncompleteRRType", po_IncompleteRRType);
+
+    if (PyType_Ready(&rrtype_type) < 0) {
+        return (false);
+    }
+    Py_INCREF(&rrtype_type);
+    PyModule_AddObject(mod, "RRType",
+                       reinterpret_cast<PyObject*>(&rrtype_type));
+
+    return (true);
+}
+
+bool
+initModulePart_TSIGError(PyObject* mod) {
+    if (PyType_Ready(&tsigerror_type) < 0) {
+        return (false);
+    }
+    void* p = &tsigerror_type;
+    if (PyModule_AddObject(mod, "TSIGError", static_cast<PyObject*>(p)) < 0) {
+        return (false);
+    }
+    Py_INCREF(&tsigerror_type);
+
+    try {
+        // Constant class variables
+        // Error codes (bare values)
+        installClassVariable(tsigerror_type, "BAD_SIG_CODE",
+                             Py_BuildValue("H", TSIGError::BAD_SIG_CODE));
+        installClassVariable(tsigerror_type, "BAD_KEY_CODE",
+                             Py_BuildValue("H", TSIGError::BAD_KEY_CODE));
+        installClassVariable(tsigerror_type, "BAD_TIME_CODE",
+                             Py_BuildValue("H", TSIGError::BAD_TIME_CODE));
+
+        // Error codes (constant objects)
+        installClassVariable(tsigerror_type, "NOERROR",
+                             createTSIGErrorObject(TSIGError::NOERROR()));
+        installClassVariable(tsigerror_type, "FORMERR",
+                             createTSIGErrorObject(TSIGError::FORMERR()));
+        installClassVariable(tsigerror_type, "SERVFAIL",
+                             createTSIGErrorObject(TSIGError::SERVFAIL()));
+        installClassVariable(tsigerror_type, "NXDOMAIN",
+                             createTSIGErrorObject(TSIGError::NXDOMAIN()));
+        installClassVariable(tsigerror_type, "NOTIMP",
+                             createTSIGErrorObject(TSIGError::NOTIMP()));
+        installClassVariable(tsigerror_type, "REFUSED",
+                             createTSIGErrorObject(TSIGError::REFUSED()));
+        installClassVariable(tsigerror_type, "YXDOMAIN",
+                             createTSIGErrorObject(TSIGError::YXDOMAIN()));
+        installClassVariable(tsigerror_type, "YXRRSET",
+                             createTSIGErrorObject(TSIGError::YXRRSET()));
+        installClassVariable(tsigerror_type, "NXRRSET",
+                             createTSIGErrorObject(TSIGError::NXRRSET()));
+        installClassVariable(tsigerror_type, "NOTAUTH",
+                             createTSIGErrorObject(TSIGError::NOTAUTH()));
+        installClassVariable(tsigerror_type, "NOTZONE",
+                             createTSIGErrorObject(TSIGError::NOTZONE()));
+        installClassVariable(tsigerror_type, "RESERVED11",
+                             createTSIGErrorObject(TSIGError::RESERVED11()));
+        installClassVariable(tsigerror_type, "RESERVED12",
+                             createTSIGErrorObject(TSIGError::RESERVED12()));
+        installClassVariable(tsigerror_type, "RESERVED13",
+                             createTSIGErrorObject(TSIGError::RESERVED13()));
+        installClassVariable(tsigerror_type, "RESERVED14",
+                             createTSIGErrorObject(TSIGError::RESERVED14()));
+        installClassVariable(tsigerror_type, "RESERVED15",
+                             createTSIGErrorObject(TSIGError::RESERVED15()));
+        installClassVariable(tsigerror_type, "BAD_SIG",
+                             createTSIGErrorObject(TSIGError::BAD_SIG()));
+        installClassVariable(tsigerror_type, "BAD_KEY",
+                             createTSIGErrorObject(TSIGError::BAD_KEY()));
+        installClassVariable(tsigerror_type, "BAD_TIME",
+                             createTSIGErrorObject(TSIGError::BAD_TIME()));
+    } catch (const std::exception& ex) {
+        const std::string ex_what =
+            "Unexpected failure in TSIGError initialization: " +
+            std::string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+        return (false);
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure in TSIGError initialization");
+        return (false);
+    }
+
+    return (true);
+}
+
+bool
+initModulePart_TSIGKey(PyObject* mod) {
+    if (PyType_Ready(&tsigkey_type) < 0) {
+        return (false);
+    }
+    void* p = &tsigkey_type;
+    if (PyModule_AddObject(mod, "TSIGKey", static_cast<PyObject*>(p)) != 0) {
+        return (false);
+    }
+    Py_INCREF(&tsigkey_type);
+
+    try {
+        // Constant class variables
+        installClassVariable(tsigkey_type, "HMACMD5_NAME",
+                             createNameObject(TSIGKey::HMACMD5_NAME()));
+        installClassVariable(tsigkey_type, "HMACSHA1_NAME",
+                             createNameObject(TSIGKey::HMACSHA1_NAME()));
+        installClassVariable(tsigkey_type, "HMACSHA256_NAME",
+                             createNameObject(TSIGKey::HMACSHA256_NAME()));
+        installClassVariable(tsigkey_type, "HMACSHA224_NAME",
+                             createNameObject(TSIGKey::HMACSHA224_NAME()));
+        installClassVariable(tsigkey_type, "HMACSHA384_NAME",
+                             createNameObject(TSIGKey::HMACSHA384_NAME()));
+        installClassVariable(tsigkey_type, "HMACSHA512_NAME",
+                             createNameObject(TSIGKey::HMACSHA512_NAME()));
+    } catch (const std::exception& ex) {
+        const std::string ex_what =
+            "Unexpected failure in TSIGKey initialization: " +
+            std::string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+        return (false);
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure in TSIGKey initialization");
+        return (false);
+    }
+
+    return (true);
+}
+
+bool
+initModulePart_TSIGKeyRing(PyObject* mod) {
+    if (PyType_Ready(&tsigkeyring_type) < 0) {
+        return (false);
+    }
+    Py_INCREF(&tsigkeyring_type);
+    void* p = &tsigkeyring_type;
+    if (PyModule_AddObject(mod, "TSIGKeyRing",
+                           static_cast<PyObject*>(p)) != 0) {
+        Py_DECREF(&tsigkeyring_type);
+        return (false);
+    }
+
+    addClassVariable(tsigkeyring_type, "SUCCESS",
+                     Py_BuildValue("I", TSIGKeyRing::SUCCESS));
+    addClassVariable(tsigkeyring_type, "EXIST",
+                     Py_BuildValue("I", TSIGKeyRing::EXIST));
+    addClassVariable(tsigkeyring_type, "NOTFOUND",
+                     Py_BuildValue("I", TSIGKeyRing::NOTFOUND));
+
+    return (true);
+}
+
+bool
+initModulePart_TSIGContext(PyObject* mod) {
+    if (PyType_Ready(&tsigcontext_type) < 0) {
+        return (false);
+    }
+    void* p = &tsigcontext_type;
+    if (PyModule_AddObject(mod, "TSIGContext",
+                           static_cast<PyObject*>(p)) < 0) {
+        return (false);
+    }
+    Py_INCREF(&tsigcontext_type);
+
+    try {
+        // Class specific exceptions
+        po_TSIGContextError = PyErr_NewException("pydnspp.TSIGContextError",
+                                                 po_IscException, NULL);
+        PyObjectContainer(po_TSIGContextError).installToModule(
+            mod, "TSIGContextError");
+
+        // Constant class variables
+        installClassVariable(tsigcontext_type, "STATE_INIT",
+                             Py_BuildValue("I", TSIGContext::INIT));
+        installClassVariable(tsigcontext_type, "STATE_SENT_REQUEST",
+                             Py_BuildValue("I", TSIGContext::SENT_REQUEST));
+        installClassVariable(tsigcontext_type, "STATE_RECEIVED_REQUEST",
+                             Py_BuildValue("I", TSIGContext::RECEIVED_REQUEST));
+        installClassVariable(tsigcontext_type, "STATE_SENT_RESPONSE",
+                             Py_BuildValue("I", TSIGContext::SENT_RESPONSE));
+        installClassVariable(tsigcontext_type, "STATE_VERIFIED_RESPONSE",
+                             Py_BuildValue("I",
+                                           TSIGContext::VERIFIED_RESPONSE));
+
+        installClassVariable(tsigcontext_type, "DEFAULT_FUDGE",
+                             Py_BuildValue("H", TSIGContext::DEFAULT_FUDGE));
+    } catch (const std::exception& ex) {
+        const std::string ex_what =
+            "Unexpected failure in TSIGContext initialization: " +
+            std::string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+        return (false);
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure in TSIGContext initialization");
+        return (false);
+    }
+
+    return (true);
+}
+
+bool
+initModulePart_TSIG(PyObject* mod) {
+    if (PyType_Ready(&tsig_type) < 0) {
+        return (false);
+    }
+    void* p = &tsig_type;
+    if (PyModule_AddObject(mod, "TSIG", static_cast<PyObject*>(p)) < 0) {
+        return (false);
+    }
+    Py_INCREF(&tsig_type);
+
+    return (true);
+}
+
+bool
+initModulePart_TSIGRecord(PyObject* mod) {
+    if (PyType_Ready(&tsigrecord_type) < 0) {
+        return (false);
+    }
+    void* p = &tsigrecord_type;
+    if (PyModule_AddObject(mod, "TSIGRecord", static_cast<PyObject*>(p)) < 0) {
+        return (false);
+    }
+    Py_INCREF(&tsigrecord_type);
+
+    try {
+        // Constant class variables
+        installClassVariable(tsigrecord_type, "TSIG_TTL",
+                             Py_BuildValue("I", 0));
+    } catch (const std::exception& ex) {
+        const std::string ex_what =
+            "Unexpected failure in TSIGRecord initialization: " +
+            std::string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+        return (false);
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure in TSIGRecord initialization");
+        return (false);
+    }
+
+    return (true);
+}
 
-//
-// Definition of the module
-//
-namespace {
 PyModuleDef pydnspp = {
     { PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
     "pydnspp",
diff --git a/src/lib/dns/python/pydnspp_common.cc b/src/lib/dns/python/pydnspp_common.cc
index 8ca763a..0f0f873 100644
--- a/src/lib/dns/python/pydnspp_common.cc
+++ b/src/lib/dns/python/pydnspp_common.cc
@@ -15,9 +15,45 @@
 #include <Python.h>
 #include <pydnspp_common.h>
 
+#include <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+
+#include <dns/exceptions.h>
+#include <dns/name.h>
+#include <dns/messagerenderer.h>
+
+#include "pydnspp_common.h"
+#include "messagerenderer_python.h"
+#include "name_python.h"
+#include "rdata_python.h"
+#include "rrclass_python.h"
+#include "rrtype_python.h"
+#include "rrttl_python.h"
+#include "rrset_python.h"
+#include "rcode_python.h"
+#include "opcode_python.h"
+#include "tsigkey_python.h"
+#include "tsig_rdata_python.h"
+#include "tsigerror_python.h"
+#include "tsigrecord_python.h"
+#include "tsig_python.h"
+#include "question_python.h"
+#include "message_python.h"
+
+using namespace isc::dns::python;
+
 namespace isc {
 namespace dns {
 namespace python {
+// For our 'general' isc::Exceptions
+PyObject* po_IscException;
+PyObject* po_InvalidParameter;
+
+// For our own isc::dns::Exception
+PyObject* po_DNSMessageBADVERS;
+
+
 int
 readDataFromSequence(uint8_t *data, size_t len, PyObject* sequence) {
     PyObject* el = NULL;
diff --git a/src/lib/dns/python/pydnspp_common.h b/src/lib/dns/python/pydnspp_common.h
index ed90998..8092b08 100644
--- a/src/lib/dns/python/pydnspp_common.h
+++ b/src/lib/dns/python/pydnspp_common.h
@@ -20,8 +20,6 @@
 #include <stdexcept>
 #include <string>
 
-#include <util/python/pycppwrapper_util.h>
-
 namespace isc {
 namespace dns {
 namespace python {
diff --git a/src/lib/dns/python/pydnspp_towire.h b/src/lib/dns/python/pydnspp_towire.h
index 66362a0..e987a29 100644
--- a/src/lib/dns/python/pydnspp_towire.h
+++ b/src/lib/dns/python/pydnspp_towire.h
@@ -93,10 +93,10 @@ toWireWrapper(const PYSTRUCT* const self, PyObject* args) {
         }
 
         // To MessageRenderer version
-        s_MessageRenderer* renderer;
+        PyObject* renderer;
         if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &renderer)) {
             const unsigned int n = TOWIRECALLER(*self->cppobj)(
-                *renderer->messagerenderer);
+                PyMessageRenderer_ToMessageRenderer(renderer));
 
             return (Py_BuildValue("I", n));
         }
diff --git a/src/lib/dns/python/question_python.cc b/src/lib/dns/python/question_python.cc
index c702f85..44d68a2 100644
--- a/src/lib/dns/python/question_python.cc
+++ b/src/lib/dns/python/question_python.cc
@@ -12,25 +12,34 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#define PY_SSIZE_T_CLEAN
+#include <Python.h>
 #include <dns/question.h>
+#include <dns/messagerenderer.h>
+#include <dns/exceptions.h>
+#include <util/buffer.h>
+#include <util/python/pycppwrapper_util.h>
+
+#include "pydnspp_common.h"
+#include "question_python.h"
+#include "name_python.h"
+#include "rrclass_python.h"
+#include "rrtype_python.h"
+#include "messagerenderer_python.h"
+
+using namespace std;
 using namespace isc::dns;
+using namespace isc::dns::python;
+using namespace isc::util;
+using namespace isc::util::python;
+using namespace isc;
 
-//
-// Question
-//
-
-// The s_* Class simply coverst one instantiation of the object
+namespace {
 class s_Question : public PyObject {
 public:
-    QuestionPtr question;
+    isc::dns::QuestionPtr cppobj;
 };
 
-//
-// We declare the functions here, the definitions are below
-// the type definition of the object, since both can use the other
-//
-
-// General creation and destruction
 static int Question_init(s_Question* self, PyObject* args);
 static void Question_destroy(s_Question* self);
 
@@ -69,60 +78,6 @@ static PyMethodDef Question_methods[] = {
     { NULL, NULL, 0, NULL }
 };
 
-// This defines the complete type for reflection in python and
-// parsing of PyObject* to s_Question
-// Most of the functions are not actually implemented and NULL here.
-static PyTypeObject question_type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "pydnspp.Question",
-    sizeof(s_Question),                 // tp_basicsize
-    0,                                  // tp_itemsize
-    (destructor)Question_destroy,       // tp_dealloc
-    NULL,                               // tp_print
-    NULL,                               // tp_getattr
-    NULL,                               // tp_setattr
-    NULL,                               // tp_reserved
-    NULL,                               // tp_repr
-    NULL,                               // tp_as_number
-    NULL,                               // tp_as_sequence
-    NULL,                               // tp_as_mapping
-    NULL,                               // tp_hash 
-    NULL,                               // tp_call
-    Question_str,                       // tp_str
-    NULL,                               // tp_getattro
-    NULL,                               // tp_setattro
-    NULL,                               // tp_as_buffer
-    Py_TPFLAGS_DEFAULT,                 // tp_flags
-    "The Question class encapsulates the common search key of DNS"
-    "lookup, consisting of owner name, RR type and RR class.",
-    NULL,                               // tp_traverse
-    NULL,                               // tp_clear
-    NULL,                               // tp_richcompare
-    0,                                  // tp_weaklistoffset
-    NULL,                               // tp_iter
-    NULL,                               // tp_iternext
-    Question_methods,                   // tp_methods
-    NULL,                               // tp_members
-    NULL,                               // tp_getset
-    NULL,                               // tp_base
-    NULL,                               // tp_dict
-    NULL,                               // tp_descr_get
-    NULL,                               // tp_descr_set
-    0,                                  // tp_dictoffset
-    (initproc)Question_init,            // tp_init
-    NULL,                               // tp_alloc
-    PyType_GenericNew,                  // tp_new
-    NULL,                               // tp_free
-    NULL,                               // tp_is_gc
-    NULL,                               // tp_bases
-    NULL,                               // tp_mro
-    NULL,                               // tp_cache
-    NULL,                               // tp_subclasses
-    NULL,                               // tp_weaklist
-    NULL,                               // tp_del
-    0                                   // tp_version_tag
-};
-
 static int
 Question_init(s_Question* self, PyObject* args) {
     // Try out the various combinations of arguments to call the
@@ -131,9 +86,9 @@ Question_init(s_Question* self, PyObject* args) {
     // that if we try several like here. Otherwise the *next* python
     // call will suddenly appear to throw an exception.
     // (the way to do exceptions is to set PyErr and return -1)
-    s_Name* name;
-    s_RRClass* rrclass;
-    s_RRType* rrtype;
+    PyObject* name;
+    PyObject* rrclass;
+    PyObject* rrtype;
 
     const char* b;
     Py_ssize_t len;
@@ -141,17 +96,18 @@ Question_init(s_Question* self, PyObject* args) {
 
     try {
         if (PyArg_ParseTuple(args, "O!O!O!", &name_type, &name,
-                                               &rrclass_type, &rrclass,
-                                               &rrtype_type, &rrtype
+                                             &rrclass_type, &rrclass,
+                                             &rrtype_type, &rrtype
            )) {
-            self->question = QuestionPtr(new Question(*name->cppobj, *rrclass->rrclass,
-                                          *rrtype->rrtype));
+            self->cppobj = QuestionPtr(new Question(PyName_ToName(name),
+                                                    PyRRClass_ToRRClass(rrclass),
+                                                    PyRRType_ToRRType(rrtype)));
             return (0);
         } else if (PyArg_ParseTuple(args, "y#|I", &b, &len, &position)) {
             PyErr_Clear();
             InputBuffer inbuf(b, len);
             inbuf.setPosition(position);
-            self->question = QuestionPtr(new Question(inbuf));
+            self->cppobj = QuestionPtr(new Question(inbuf));
             return (0);
         }
     } catch (const DNSMessageFORMERR& dmfe) {
@@ -168,7 +124,7 @@ Question_init(s_Question* self, PyObject* args) {
         return (-1);
     }
 
-    self->question = QuestionPtr();
+    self->cppobj = QuestionPtr();
 
     PyErr_Clear();
     PyErr_SetString(PyExc_TypeError,
@@ -178,52 +134,62 @@ Question_init(s_Question* self, PyObject* args) {
 
 static void
 Question_destroy(s_Question* self) {
-    self->question.reset();
+    self->cppobj.reset();
     Py_TYPE(self)->tp_free(self);
 }
 
 static PyObject*
 Question_getName(s_Question* self) {
-    s_Name* name;
-
-    // is this the best way to do this?
-    name = static_cast<s_Name*>(name_type.tp_alloc(&name_type, 0));
-    if (name != NULL) {
-        name->cppobj = new Name(self->question->getName());
+    try {
+        return (createNameObject(self->cppobj->getName()));
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Unexpected failure getting question Name: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure getting question Name");
     }
-
-    return (name);
+    return (NULL);
 }
 
 static PyObject*
 Question_getType(s_Question* self) {
-    s_RRType* rrtype;
-
-    rrtype = static_cast<s_RRType*>(rrtype_type.tp_alloc(&rrtype_type, 0));
-    if (rrtype != NULL) {
-        rrtype->rrtype = new RRType(self->question->getType());
+    try {
+        return (createRRTypeObject(self->cppobj->getType()));
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Unexpected failure getting question RRType: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure getting question RRType");
     }
-
-    return (rrtype);
+    return (NULL);
 }
 
 static PyObject*
 Question_getClass(s_Question* self) {
-    s_RRClass* rrclass;
-
-    rrclass = static_cast<s_RRClass*>(rrclass_type.tp_alloc(&rrclass_type, 0));
-    if (rrclass != NULL) {
-        rrclass->rrclass = new RRClass(self->question->getClass());
+    try {
+        return (createRRClassObject(self->cppobj->getClass()));
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Unexpected failure getting question RRClass: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure getting question RRClass");
     }
-
-    return (rrclass);
+    return (NULL);
 }
 
-
 static PyObject*
 Question_toText(s_Question* self) {
     // Py_BuildValue makes python objects from native data
-    return (Py_BuildValue("s", self->question->toText().c_str()));
+    return (Py_BuildValue("s", self->cppobj->toText().c_str()));
 }
 
 static PyObject*
@@ -237,14 +203,14 @@ Question_str(PyObject* self) {
 static PyObject*
 Question_toWire(s_Question* self, PyObject* args) {
     PyObject* bytes;
-    s_MessageRenderer* mr;
-    
+    PyObject* mr;
+
     if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) {
         PyObject* bytes_o = bytes;
 
         // Max length is Name::MAX_WIRE + rrclass (2) + rrtype (2)
         OutputBuffer buffer(Name::MAX_WIRE + 4);
-        self->question->toWire(buffer);
+        self->cppobj->toWire(buffer);
         PyObject* n = PyBytes_FromStringAndSize(static_cast<const char*>(buffer.getData()),
                                                 buffer.getLength());
         PyObject* result = PySequence_InPlaceConcat(bytes_o, n);
@@ -253,7 +219,7 @@ Question_toWire(s_Question* self, PyObject* args) {
         Py_DECREF(n);
         return (result);
     } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
-        self->question->toWire(*mr->messagerenderer);
+        self->cppobj->toWire(PyMessageRenderer_ToMessageRenderer(mr));
         // If we return NULL it is seen as an error, so use this for
         // None returns
         Py_RETURN_NONE;
@@ -264,23 +230,92 @@ Question_toWire(s_Question* self, PyObject* args) {
     return (NULL);
 }
 
-// end of Question
+} // end of unnamed namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_Question
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject question_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "pydnspp.Question",
+    sizeof(s_Question),                 // tp_basicsize
+    0,                                  // tp_itemsize
+    (destructor)Question_destroy,       // tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash
+    NULL,                               // tp_call
+    Question_str,                       // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    "The Question class encapsulates the common search key of DNS"
+    "lookup, consisting of owner name, RR type and RR class.",
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    NULL,                               // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    Question_methods,                   // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    (initproc)Question_init,            // tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
 
+PyObject*
+createQuestionObject(const Question& source) {
+    s_Question* question =
+        static_cast<s_Question*>(question_type.tp_alloc(&question_type, 0));
+    question->cppobj = QuestionPtr(new Question(source));
+    return (question);
+}
 
-// Module Initialization, all statics are initialized here
 bool
-initModulePart_Question(PyObject* mod) {
-    // Add the exceptions to the module
+PyQuestion_Check(PyObject* obj) {
+    if (obj == NULL) {
+        isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck");
+    }
+    return (PyObject_TypeCheck(obj, &question_type));
+}
 
-    // We initialize the static description object with PyType_Ready(),
-    // then add it to the module. This is not just a check! (leaving
-    // this out results in segmentation faults)
-    if (PyType_Ready(&question_type) < 0) {
-        return (false);
+const Question&
+PyQuestion_ToQuestion(const PyObject* question_obj) {
+    if (question_obj == NULL) {
+        isc_throw(PyCPPWrapperException,
+                  "obj argument NULL in Question PyObject conversion");
     }
-    Py_INCREF(&question_type);
-    PyModule_AddObject(mod, "Question",
-                       reinterpret_cast<PyObject*>(&question_type));
-    
-    return (true);
+    const s_Question* question = static_cast<const s_Question*>(question_obj);
+    return (*question->cppobj);
 }
+
+} // end python namespace
+} // end dns namespace
+} // end isc namespace
diff --git a/src/lib/dns/python/question_python.h b/src/lib/dns/python/question_python.h
new file mode 100644
index 0000000..f5d78b1
--- /dev/null
+++ b/src/lib/dns/python/question_python.h
@@ -0,0 +1,66 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_QUESTION_H
+#define __PYTHON_QUESTION_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace dns {
+class Question;
+
+namespace python {
+
+extern PyObject* po_EmptyQuestion;
+
+extern PyTypeObject question_type;
+
+/// This is a simple shortcut to create a python Question object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called within a try block
+/// followed by necessary setup for python exception.
+PyObject* createQuestionObject(const Question& source);
+
+/// \brief Checks if the given python object is a Question object
+///
+/// \exception PyCPPWrapperException if obj is NULL
+///
+/// \param obj The object to check the type of
+/// \return true if the object is of type Question, false otherwise
+bool PyQuestion_Check(PyObject* obj);
+
+/// \brief Returns a reference to the Question object contained within the given
+///        Python object.
+///
+/// \note The given object MUST be of type Question; this can be checked with
+///       either the right call to ParseTuple("O!"), or with PyQuestion_Check()
+///
+/// \note This is not a copy; if the Question is needed when the PyObject
+/// may be destroyed, the caller must copy it itself.
+///
+/// \param question_obj The question object to convert
+const Question& PyQuestion_ToQuestion(const PyObject* question_obj);
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_QUESTION_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/rcode_python.cc b/src/lib/dns/python/rcode_python.cc
index b594ad3..42b48e7 100644
--- a/src/lib/dns/python/rcode_python.cc
+++ b/src/lib/dns/python/rcode_python.cc
@@ -15,34 +15,39 @@
 #include <Python.h>
 
 #include <exceptions/exceptions.h>
-
 #include <dns/rcode.h>
+#include <util/python/pycppwrapper_util.h>
 
 #include "pydnspp_common.h"
 #include "rcode_python.h"
 
 using namespace isc::dns;
 using namespace isc::dns::python;
+using namespace isc::util::python;
 
+namespace {
+// The s_* Class simply covers one instantiation of the object.
 //
-// Declaration of the custom exceptions (None for this class)
-
-//
-// Definition of the classes
-//
-
-// For each class, we need a struct, a helper functions (init, destroy,
-// and static wrappers around the methods we export), a list of methods,
-// and a type description
-
-//
-// Rcode
+// We added a helper variable static_code here
+// Since we can create Rcodes dynamically with Rcode(int), but also
+// use the static globals (Rcode::NOERROR() etc), we use this
+// variable to see if the code came from one of the latter, in which
+// case Rcode_destroy should not free it (the other option is to
+// allocate new Rcodes for every use of the static ones, but this
+// seems more efficient).
 //
+// Follow-up note: we don't have to use the proxy function in the python lib;
+// we can just define class specific constants directly (see TSIGError).
+// We should make this cleanup later.
+class s_Rcode : public PyObject {
+public:
+    s_Rcode() : cppobj(NULL), static_code(false) {};
+    const Rcode* cppobj;
+    bool static_code;
+};
 
-// Trivial constructor.
-s_Rcode::s_Rcode() : cppobj(NULL), static_code(false) {}
+typedef CPPPyObjectContainer<s_Rcode, Rcode> RcodeContainer;
 
-namespace {
 int Rcode_init(s_Rcode* const self, PyObject* args);
 void Rcode_destroy(s_Rcode* const self);
 
@@ -282,7 +287,7 @@ Rcode_BADVERS(const s_Rcode*) {
     return (Rcode_createStatic(Rcode::BADVERS()));
 }
 
-PyObject* 
+PyObject*
 Rcode_richcmp(const s_Rcode* const self, const s_Rcode* const other,
               const int op)
 {
@@ -376,59 +381,31 @@ PyTypeObject rcode_type = {
     0                                   // tp_version_tag
 };
 
-// Module Initialization, all statics are initialized here
+PyObject*
+createRcodeObject(const Rcode& source) {
+    RcodeContainer container(PyObject_New(s_Rcode, &rcode_type));
+    container.set(new Rcode(source));
+    return (container.release());
+}
+
 bool
-initModulePart_Rcode(PyObject* mod) {
-    // We initialize the static description object with PyType_Ready(),
-    // then add it to the module. This is not just a check! (leaving
-    // this out results in segmentation faults)
-    if (PyType_Ready(&rcode_type) < 0) {
-        return (false);
-    }
-    Py_INCREF(&rcode_type);
-    void* p = &rcode_type;
-    if (PyModule_AddObject(mod, "Rcode", static_cast<PyObject*>(p)) != 0) {
-        Py_DECREF(&rcode_type);
-        return (false);
+PyRcode_Check(PyObject* obj) {
+    if (obj == NULL) {
+        isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck");
     }
+    return (PyObject_TypeCheck(obj, &rcode_type));
+}
 
-    addClassVariable(rcode_type, "NOERROR_CODE",
-                     Py_BuildValue("h", Rcode::NOERROR_CODE));
-    addClassVariable(rcode_type, "FORMERR_CODE",
-                     Py_BuildValue("h", Rcode::FORMERR_CODE));
-    addClassVariable(rcode_type, "SERVFAIL_CODE",
-                     Py_BuildValue("h", Rcode::SERVFAIL_CODE));
-    addClassVariable(rcode_type, "NXDOMAIN_CODE",
-                     Py_BuildValue("h", Rcode::NXDOMAIN_CODE));
-    addClassVariable(rcode_type, "NOTIMP_CODE",
-                     Py_BuildValue("h", Rcode::NOTIMP_CODE));
-    addClassVariable(rcode_type, "REFUSED_CODE",
-                     Py_BuildValue("h", Rcode::REFUSED_CODE));
-    addClassVariable(rcode_type, "YXDOMAIN_CODE",
-                     Py_BuildValue("h", Rcode::YXDOMAIN_CODE));
-    addClassVariable(rcode_type, "YXRRSET_CODE",
-                     Py_BuildValue("h", Rcode::YXRRSET_CODE));
-    addClassVariable(rcode_type, "NXRRSET_CODE",
-                     Py_BuildValue("h", Rcode::NXRRSET_CODE));
-    addClassVariable(rcode_type, "NOTAUTH_CODE",
-                     Py_BuildValue("h", Rcode::NOTAUTH_CODE));
-    addClassVariable(rcode_type, "NOTZONE_CODE",
-                     Py_BuildValue("h", Rcode::NOTZONE_CODE));
-    addClassVariable(rcode_type, "RESERVED11_CODE",
-                     Py_BuildValue("h", Rcode::RESERVED11_CODE));
-    addClassVariable(rcode_type, "RESERVED12_CODE",
-                     Py_BuildValue("h", Rcode::RESERVED12_CODE));
-    addClassVariable(rcode_type, "RESERVED13_CODE",
-                     Py_BuildValue("h", Rcode::RESERVED13_CODE));
-    addClassVariable(rcode_type, "RESERVED14_CODE",
-                     Py_BuildValue("h", Rcode::RESERVED14_CODE));
-    addClassVariable(rcode_type, "RESERVED15_CODE",
-                     Py_BuildValue("h", Rcode::RESERVED15_CODE));
-    addClassVariable(rcode_type, "BADVERS_CODE",
-                     Py_BuildValue("h", Rcode::BADVERS_CODE));
-
-    return (true);
+const Rcode&
+PyRcode_ToRcode(const PyObject* rcode_obj) {
+    if (rcode_obj == NULL) {
+        isc_throw(PyCPPWrapperException,
+                  "obj argument NULL in Rcode PyObject conversion");
+    }
+    const s_Rcode* rcode = static_cast<const s_Rcode*>(rcode_obj);
+    return (*rcode->cppobj);
 }
+
 } // namespace python
 } // namespace dns
 } // namespace isc
diff --git a/src/lib/dns/python/rcode_python.h b/src/lib/dns/python/rcode_python.h
index 9b5e699..a149406 100644
--- a/src/lib/dns/python/rcode_python.h
+++ b/src/lib/dns/python/rcode_python.h
@@ -23,29 +23,36 @@ class Rcode;
 
 namespace python {
 
-// The s_* Class simply covers one instantiation of the object.
-//
-// We added a helper variable static_code here
-// Since we can create Rcodes dynamically with Rcode(int), but also
-// use the static globals (Rcode::NOERROR() etc), we use this
-// variable to see if the code came from one of the latter, in which
-// case Rcode_destroy should not free it (the other option is to
-// allocate new Rcodes for every use of the static ones, but this
-// seems more efficient).
-//
-// Follow-up note: we don't have to use the proxy function in the python lib;
-// we can just define class specific constants directly (see TSIGError).
-// We should make this cleanup later.
-class s_Rcode : public PyObject {
-public:
-    s_Rcode();
-    const Rcode* cppobj;
-    bool static_code;
-};
-
 extern PyTypeObject rcode_type;
 
-bool initModulePart_Rcode(PyObject* mod);
+/// This is a simple shortcut to create a python Rcode object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called within a try block
+/// followed by necessary setup for python exception.
+PyObject* createRcodeObject(const Rcode& source);
+
+/// \brief Checks if the given python object is a Rcode object
+///
+/// \exception PyCPPWrapperException if obj is NULL
+///
+/// \param obj The object to check the type of
+/// \return true if the object is of type Rcode, false otherwise
+bool PyRcode_Check(PyObject* obj);
+
+/// \brief Returns a reference to the Rcode object contained within the given
+///        Python object.
+///
+/// \note The given object MUST be of type Rcode; this can be checked with
+///       either the right call to ParseTuple("O!"), or with PyRcode_Check()
+///
+/// \note This is not a copy; if the Rcode is needed when the PyObject
+/// may be destroyed, the caller must copy it itself.
+///
+/// \param rcode_obj The rcode object to convert
+const Rcode& PyRcode_ToRcode(const PyObject* rcode_obj);
 
 } // namespace python
 } // namespace dns
diff --git a/src/lib/dns/python/rdata_python.cc b/src/lib/dns/python/rdata_python.cc
index faa4f4c..06c0263 100644
--- a/src/lib/dns/python/rdata_python.cc
+++ b/src/lib/dns/python/rdata_python.cc
@@ -12,60 +12,48 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#define PY_SSIZE_T_CLEAN
+#include <Python.h>
 #include <dns/rdata.h>
+#include <dns/messagerenderer.h>
+#include <util/buffer.h>
+#include <util/python/pycppwrapper_util.h>
+
+#include "rdata_python.h"
+#include "rrtype_python.h"
+#include "rrclass_python.h"
+#include "messagerenderer_python.h"
+
 using namespace isc::dns;
+using namespace isc::dns::python;
 using namespace isc::util;
+using namespace isc::util::python;
 using namespace isc::dns::rdata;
 
-//
-// Declaration of the custom exceptions
-// Initialization and addition of these go in the initModulePart
-// function at the end of this file
-//
-static PyObject* po_InvalidRdataLength;
-static PyObject* po_InvalidRdataText;
-static PyObject* po_CharStringTooLong;
-
-//
-// Definition of the classes
-//
-
-// For each class, we need a struct, a helper functions (init, destroy,
-// and static wrappers around the methods we export), a list of methods,
-// and a type description
-
-//
-// Rdata
-//
-
-// The s_* Class simply coverst one instantiation of the object
-
-// Using a shared_ptr here should not really be necessary (PyObject
-// is already reference-counted), however internally on the cpp side,
-// not doing so might result in problems, since we can't copy construct
-// rdata field, adding them to rrsets results in a problem when the
-// rrset is destroyed later
+namespace {
 class s_Rdata : public PyObject {
 public:
-    RdataPtr rdata;
+    isc::dns::rdata::ConstRdataPtr cppobj;
 };
 
+typedef CPPPyObjectContainer<s_Rdata, Rdata> RdataContainer;
+
 //
 // We declare the functions here, the definitions are below
 // the type definition of the object, since both can use the other
 //
 
 // General creation and destruction
-static int Rdata_init(s_Rdata* self, PyObject* args);
-static void Rdata_destroy(s_Rdata* self);
+int Rdata_init(s_Rdata* self, PyObject* args);
+void Rdata_destroy(s_Rdata* self);
 
 // These are the functions we export
-static PyObject* Rdata_toText(s_Rdata* self);
+PyObject* Rdata_toText(s_Rdata* self);
 // This is a second version of toText, we need one where the argument
 // is a PyObject*, for the str() function in python.
-static PyObject* Rdata_str(PyObject* self);
-static PyObject* Rdata_toWire(s_Rdata* self, PyObject* args);
-static PyObject* RData_richcmp(s_Rdata* self, s_Rdata* other, int op);
+PyObject* Rdata_str(PyObject* self);
+PyObject* Rdata_toWire(s_Rdata* self, PyObject* args);
+PyObject* RData_richcmp(s_Rdata* self, s_Rdata* other, int op);
 
 // This list contains the actual set of functions we have in
 // python. Each entry has
@@ -73,7 +61,7 @@ static PyObject* RData_richcmp(s_Rdata* self, s_Rdata* other, int op);
 // 2. Our static function here
 // 3. Argument type
 // 4. Documentation
-static PyMethodDef Rdata_methods[] = {
+PyMethodDef Rdata_methods[] = {
     { "to_text", reinterpret_cast<PyCFunction>(Rdata_toText), METH_NOARGS,
       "Returns the string representation" },
     { "to_wire", reinterpret_cast<PyCFunction>(Rdata_toWire), METH_VARARGS,
@@ -86,64 +74,10 @@ static PyMethodDef Rdata_methods[] = {
     { NULL, NULL, 0, NULL }
 };
 
-// This defines the complete type for reflection in python and
-// parsing of PyObject* to s_Rdata
-// Most of the functions are not actually implemented and NULL here.
-static PyTypeObject rdata_type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "pydnspp.Rdata",
-    sizeof(s_Rdata),                    // tp_basicsize
-    0,                                  // tp_itemsize
-    (destructor)Rdata_destroy,          // tp_dealloc
-    NULL,                               // tp_print
-    NULL,                               // tp_getattr
-    NULL,                               // tp_setattr
-    NULL,                               // tp_reserved
-    NULL,                               // tp_repr
-    NULL,                               // tp_as_number
-    NULL,                               // tp_as_sequence
-    NULL,                               // tp_as_mapping
-    NULL,                               // tp_hash 
-    NULL,                               // tp_call
-    Rdata_str,                          // tp_str
-    NULL,                               // tp_getattro
-    NULL,                               // tp_setattro
-    NULL,                               // tp_as_buffer
-    Py_TPFLAGS_DEFAULT,                 // tp_flags
-    "The Rdata class is an abstract base class that provides "
-    "a set of common interfaces to manipulate concrete RDATA objects.",
-    NULL,                               // tp_traverse
-    NULL,                               // tp_clear
-    (richcmpfunc)RData_richcmp,         // tp_richcompare
-    0,                                  // tp_weaklistoffset
-    NULL,                               // tp_iter
-    NULL,                               // tp_iternext
-    Rdata_methods,                      // tp_methods
-    NULL,                               // tp_members
-    NULL,                               // tp_getset
-    NULL,                               // tp_base
-    NULL,                               // tp_dict
-    NULL,                               // tp_descr_get
-    NULL,                               // tp_descr_set
-    0,                                  // tp_dictoffset
-    (initproc)Rdata_init,               // tp_init
-    NULL,                               // tp_alloc
-    PyType_GenericNew,                  // tp_new
-    NULL,                               // tp_free
-    NULL,                               // tp_is_gc
-    NULL,                               // tp_bases
-    NULL,                               // tp_mro
-    NULL,                               // tp_cache
-    NULL,                               // tp_subclasses
-    NULL,                               // tp_weaklist
-    NULL,                               // tp_del
-    0                                   // tp_version_tag
-};
-
-static int
+int
 Rdata_init(s_Rdata* self, PyObject* args) {
-    s_RRType* rrtype;
-    s_RRClass* rrclass;
+    PyObject* rrtype;
+    PyObject* rrclass;
     const char* s;
     const char* data;
     Py_ssize_t len;
@@ -152,34 +86,36 @@ Rdata_init(s_Rdata* self, PyObject* args) {
     if (PyArg_ParseTuple(args, "O!O!s", &rrtype_type, &rrtype,
                                         &rrclass_type, &rrclass,
                                         &s)) {
-        self->rdata = createRdata(*rrtype->rrtype, *rrclass->rrclass, s);
+        self->cppobj = createRdata(PyRRType_ToRRType(rrtype),
+                                   PyRRClass_ToRRClass(rrclass), s);
         return (0);
     } else if (PyArg_ParseTuple(args, "O!O!y#", &rrtype_type, &rrtype,
                                 &rrclass_type, &rrclass, &data, &len)) {
         InputBuffer input_buffer(data, len);
-        self->rdata = createRdata(*rrtype->rrtype, *rrclass->rrclass,
-                                  input_buffer, len);
+        self->cppobj = createRdata(PyRRType_ToRRType(rrtype),
+                                   PyRRClass_ToRRClass(rrclass),
+                                   input_buffer, len);
         return (0);
     }
 
     return (-1);
 }
 
-static void
+void
 Rdata_destroy(s_Rdata* self) {
     // Clear the shared_ptr so that its reference count is zero
     // before we call tp_free() (there is no direct release())
-    self->rdata.reset();
+    self->cppobj.reset();
     Py_TYPE(self)->tp_free(self);
 }
 
-static PyObject*
+PyObject*
 Rdata_toText(s_Rdata* self) {
     // Py_BuildValue makes python objects from native data
-    return (Py_BuildValue("s", self->rdata->toText().c_str()));
+    return (Py_BuildValue("s", self->cppobj->toText().c_str()));
 }
 
-static PyObject*
+PyObject*
 Rdata_str(PyObject* self) {
     // Simply call the to_text method we already defined
     return (PyObject_CallMethod(self,
@@ -187,16 +123,16 @@ Rdata_str(PyObject* self) {
                                 const_cast<char*>("")));
 }
 
-static PyObject*
+PyObject*
 Rdata_toWire(s_Rdata* self, PyObject* args) {
     PyObject* bytes;
-    s_MessageRenderer* mr;
-    
+    PyObject* mr;
+
     if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) {
         PyObject* bytes_o = bytes;
-        
+
         OutputBuffer buffer(4);
-        self->rdata->toWire(buffer);
+        self->cppobj->toWire(buffer);
         PyObject* rd_bytes = PyBytes_FromStringAndSize(static_cast<const char*>(buffer.getData()), buffer.getLength());
         PyObject* result = PySequence_InPlaceConcat(bytes_o, rd_bytes);
         // We need to release the object we temporarily created here
@@ -204,7 +140,7 @@ Rdata_toWire(s_Rdata* self, PyObject* args) {
         Py_DECREF(rd_bytes);
         return (result);
     } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
-        self->rdata->toWire(*mr->messagerenderer);
+        self->cppobj->toWire(PyMessageRenderer_ToMessageRenderer(mr));
         // If we return NULL it is seen as an error, so use this for
         // None returns
         Py_RETURN_NONE;
@@ -215,9 +151,7 @@ Rdata_toWire(s_Rdata* self, PyObject* args) {
     return (NULL);
 }
 
-
-
-static PyObject* 
+PyObject*
 RData_richcmp(s_Rdata* self, s_Rdata* other, int op) {
     bool c;
 
@@ -229,24 +163,24 @@ RData_richcmp(s_Rdata* self, s_Rdata* other, int op) {
 
     switch (op) {
     case Py_LT:
-        c = self->rdata->compare(*other->rdata) < 0;
+        c = self->cppobj->compare(*other->cppobj) < 0;
         break;
     case Py_LE:
-        c = self->rdata->compare(*other->rdata) < 0 ||
-            self->rdata->compare(*other->rdata) == 0;
+        c = self->cppobj->compare(*other->cppobj) < 0 ||
+            self->cppobj->compare(*other->cppobj) == 0;
         break;
     case Py_EQ:
-        c = self->rdata->compare(*other->rdata) == 0;
+        c = self->cppobj->compare(*other->cppobj) == 0;
         break;
     case Py_NE:
-        c = self->rdata->compare(*other->rdata) != 0;
+        c = self->cppobj->compare(*other->cppobj) != 0;
         break;
     case Py_GT:
-        c = self->rdata->compare(*other->rdata) > 0;
+        c = self->cppobj->compare(*other->cppobj) > 0;
         break;
     case Py_GE:
-        c = self->rdata->compare(*other->rdata) > 0 ||
-            self->rdata->compare(*other->rdata) == 0;
+        c = self->cppobj->compare(*other->cppobj) > 0 ||
+            self->cppobj->compare(*other->cppobj) == 0;
         break;
     default:
         PyErr_SetString(PyExc_IndexError,
@@ -258,32 +192,107 @@ RData_richcmp(s_Rdata* self, s_Rdata* other, int op) {
     else
         Py_RETURN_FALSE;
 }
-// end of Rdata
 
+} // end of unnamed namespace
 
-// Module Initialization, all statics are initialized here
-bool
-initModulePart_Rdata(PyObject* mod) {
-    // We initialize the static description object with PyType_Ready(),
-    // then add it to the module. This is not just a check! (leaving
-    // this out results in segmentation faults)
-    if (PyType_Ready(&rdata_type) < 0) {
-        return (false);
-    }
-    Py_INCREF(&rdata_type);
-    PyModule_AddObject(mod, "Rdata",
-                       reinterpret_cast<PyObject*>(&rdata_type));
+namespace isc {
+namespace dns {
+namespace python {
 
-    // Add the exceptions to the class
-    po_InvalidRdataLength = PyErr_NewException("pydnspp.InvalidRdataLength", NULL, NULL);
-    PyModule_AddObject(mod, "InvalidRdataLength", po_InvalidRdataLength);
 
-    po_InvalidRdataText = PyErr_NewException("pydnspp.InvalidRdataText", NULL, NULL);
-    PyModule_AddObject(mod, "InvalidRdataText", po_InvalidRdataText);
+//
+// Declaration of the custom exceptions
+// Initialization and addition of these go in the initModulePart
+// function in pydnspp
+//
+PyObject* po_InvalidRdataLength;
+PyObject* po_InvalidRdataText;
+PyObject* po_CharStringTooLong;
 
-    po_CharStringTooLong = PyErr_NewException("pydnspp.CharStringTooLong", NULL, NULL);
-    PyModule_AddObject(mod, "CharStringTooLong", po_CharStringTooLong);
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_Rdata
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject rdata_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "pydnspp.Rdata",
+    sizeof(s_Rdata),                    // tp_basicsize
+    0,                                  // tp_itemsize
+    (destructor)Rdata_destroy,          // tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash
+    NULL,                               // tp_call
+    Rdata_str,                          // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    "The Rdata class is an abstract base class that provides "
+    "a set of common interfaces to manipulate concrete RDATA objects.",
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    (richcmpfunc)RData_richcmp,         // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    Rdata_methods,                      // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    (initproc)Rdata_init,               // tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
 
-    
-    return (true);
+PyObject*
+createRdataObject(ConstRdataPtr source) {
+    s_Rdata* py_rdata =
+        static_cast<s_Rdata*>(rdata_type.tp_alloc(&rdata_type, 0));
+    if (py_rdata == NULL) {
+        isc_throw(PyCPPWrapperException, "Unexpected NULL C++ object, "
+                  "probably due to short memory");
+    }
+    py_rdata->cppobj = source;
+    return (py_rdata);
 }
+
+bool
+PyRdata_Check(PyObject* obj) {
+    if (obj == NULL) {
+        isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck");
+    }
+    return (PyObject_TypeCheck(obj, &rdata_type));
+}
+
+const Rdata&
+PyRdata_ToRdata(const PyObject* rdata_obj) {
+    if (rdata_obj == NULL) {
+        isc_throw(PyCPPWrapperException,
+                  "obj argument NULL in Rdata PyObject conversion");
+    }
+    const s_Rdata* rdata = static_cast<const s_Rdata*>(rdata_obj);
+    return (*rdata->cppobj);
+}
+
+} // end python namespace
+} // end dns namespace
+} // end isc namespace
diff --git a/src/lib/dns/python/rdata_python.h b/src/lib/dns/python/rdata_python.h
new file mode 100644
index 0000000..c7ddd57
--- /dev/null
+++ b/src/lib/dns/python/rdata_python.h
@@ -0,0 +1,68 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_RDATA_H
+#define __PYTHON_RDATA_H 1
+
+#include <Python.h>
+
+#include <dns/rdata.h>
+
+namespace isc {
+namespace dns {
+namespace python {
+
+extern PyObject* po_InvalidRdataLength;
+extern PyObject* po_InvalidRdataText;
+extern PyObject* po_CharStringTooLong;
+
+extern PyTypeObject rdata_type;
+
+/// This is a simple shortcut to create a python Rdata object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called within a try block
+/// followed by necessary setup for python exception.
+PyObject* createRdataObject(isc::dns::rdata::ConstRdataPtr source);
+
+/// \brief Checks if the given python object is a Rdata object
+///
+/// \exception PyCPPWrapperException if obj is NULL
+///
+/// \param obj The object to check the type of
+/// \return true if the object is of type Rdata, false otherwise
+bool PyRdata_Check(PyObject* obj);
+
+/// \brief Returns a reference to the Rdata object contained within the given
+///        Python object.
+///
+/// \note The given object MUST be of type Rdata; this can be checked with
+///       either the right call to ParseTuple("O!"), or with PyRdata_Check()
+///
+/// \note This is not a copy; if the Rdata is needed when the PyObject
+/// may be destroyed, the caller must copy it itself.
+///
+/// \param rdata_obj The rdata object to convert
+const isc::dns::rdata::Rdata& PyRdata_ToRdata(const PyObject* rdata_obj);
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_RDATA_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/rrclass_python.cc b/src/lib/dns/python/rrclass_python.cc
index 6d150c2..0014187 100644
--- a/src/lib/dns/python/rrclass_python.cc
+++ b/src/lib/dns/python/rrclass_python.cc
@@ -11,35 +11,28 @@
 // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
+#include <Python.h>
 
 #include <dns/rrclass.h>
-using namespace isc::dns;
-using namespace isc::util;
-
-//
-// Declaration of the custom exceptions
-// Initialization and addition of these go in the initModulePart
-// function at the end of this file
-//
-static PyObject* po_InvalidRRClass;
-static PyObject* po_IncompleteRRClass;
-
-//
-// Definition of the classes
-//
+#include <dns/messagerenderer.h>
+#include <util/buffer.h>
+#include <util/python/pycppwrapper_util.h>
 
-// For each class, we need a struct, a helper functions (init, destroy,
-// and static wrappers around the methods we export), a list of methods,
-// and a type description
+#include "rrclass_python.h"
+#include "messagerenderer_python.h"
+#include "pydnspp_common.h"
 
-//
-// RRClass
-//
 
+using namespace isc::dns;
+using namespace isc::dns::python;
+using namespace isc::util;
+using namespace isc::util::python;
+namespace {
 // The s_* Class simply covers one instantiation of the object
 class s_RRClass : public PyObject {
 public:
-    RRClass* rrclass;
+    s_RRClass() : cppobj(NULL) {};
+    RRClass* cppobj;
 };
 
 //
@@ -48,25 +41,26 @@ public:
 //
 
 // General creation and destruction
-static int RRClass_init(s_RRClass* self, PyObject* args);
-static void RRClass_destroy(s_RRClass* self);
+int RRClass_init(s_RRClass* self, PyObject* args);
+void RRClass_destroy(s_RRClass* self);
 
 // These are the functions we export
-static PyObject* RRClass_toText(s_RRClass* self);
+PyObject* RRClass_toText(s_RRClass* self);
 // This is a second version of toText, we need one where the argument
 // is a PyObject*, for the str() function in python.
-static PyObject* RRClass_str(PyObject* self);
-static PyObject* RRClass_toWire(s_RRClass* self, PyObject* args);
-static PyObject* RRClass_getCode(s_RRClass* self);
-static PyObject* RRClass_richcmp(s_RRClass* self, s_RRClass* other, int op);
+PyObject* RRClass_str(PyObject* self);
+PyObject* RRClass_toWire(s_RRClass* self, PyObject* args);
+PyObject* RRClass_getCode(s_RRClass* self);
+PyObject* RRClass_richcmp(s_RRClass* self, s_RRClass* other, int op);
 
 // Static function for direct class creation
-static PyObject* RRClass_IN(s_RRClass *self);
-static PyObject* RRClass_CH(s_RRClass *self);
-static PyObject* RRClass_HS(s_RRClass *self);
-static PyObject* RRClass_NONE(s_RRClass *self);
-static PyObject* RRClass_ANY(s_RRClass *self);
+PyObject* RRClass_IN(s_RRClass *self);
+PyObject* RRClass_CH(s_RRClass *self);
+PyObject* RRClass_HS(s_RRClass *self);
+PyObject* RRClass_NONE(s_RRClass *self);
+PyObject* RRClass_ANY(s_RRClass *self);
 
+typedef CPPPyObjectContainer<s_RRClass, RRClass> RRClassContainer;
 
 // This list contains the actual set of functions we have in
 // python. Each entry has
@@ -74,7 +68,7 @@ static PyObject* RRClass_ANY(s_RRClass *self);
 // 2. Our static function here
 // 3. Argument type
 // 4. Documentation
-static PyMethodDef RRClass_methods[] = {
+PyMethodDef RRClass_methods[] = {
     { "to_text", reinterpret_cast<PyCFunction>(RRClass_toText), METH_NOARGS,
       "Returns the string representation" },
     { "to_wire", reinterpret_cast<PyCFunction>(RRClass_toWire), METH_VARARGS,
@@ -94,63 +88,7 @@ static PyMethodDef RRClass_methods[] = {
     { NULL, NULL, 0, NULL }
 };
 
-// This defines the complete type for reflection in python and
-// parsing of PyObject* to s_RRClass
-// Most of the functions are not actually implemented and NULL here.
-static PyTypeObject rrclass_type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "pydnspp.RRClass",
-    sizeof(s_RRClass),                  // tp_basicsize
-    0,                                  // tp_itemsize
-    (destructor)RRClass_destroy,        // tp_dealloc
-    NULL,                               // tp_print
-    NULL,                               // tp_getattr
-    NULL,                               // tp_setattr
-    NULL,                               // tp_reserved
-    NULL,                               // tp_repr
-    NULL,                               // tp_as_number
-    NULL,                               // tp_as_sequence
-    NULL,                               // tp_as_mapping
-    NULL,                               // tp_hash 
-    NULL,                               // tp_call
-    RRClass_str,                        // tp_str
-    NULL,                               // tp_getattro
-    NULL,                               // tp_setattro
-    NULL,                               // tp_as_buffer
-    Py_TPFLAGS_DEFAULT,                 // tp_flags
-    "The RRClass class encapsulates DNS resource record classes.\n"
-    "This class manages the 16-bit integer class codes in quite a straightforward"
-    "way.  The only non trivial task is to handle textual representations of"
-    "RR classes, such as \"IN\", \"CH\", or \"CLASS65534\".",
-    NULL,                               // tp_traverse
-    NULL,                               // tp_clear
-    (richcmpfunc)RRClass_richcmp,       // tp_richcompare
-    0,                                  // tp_weaklistoffset
-    NULL,                               // tp_iter
-    NULL,                               // tp_iternext
-    RRClass_methods,                    // tp_methods
-    NULL,                               // tp_members
-    NULL,                               // tp_getset
-    NULL,                               // tp_base
-    NULL,                               // tp_dict
-    NULL,                               // tp_descr_get
-    NULL,                               // tp_descr_set
-    0,                                  // tp_dictoffset
-    (initproc)RRClass_init,             // tp_init
-    NULL,                               // tp_alloc
-    PyType_GenericNew,                  // tp_new
-    NULL,                               // tp_free
-    NULL,                               // tp_is_gc
-    NULL,                               // tp_bases
-    NULL,                               // tp_mro
-    NULL,                               // tp_cache
-    NULL,                               // tp_subclasses
-    NULL,                               // tp_weaklist
-    NULL,                               // tp_del
-    0                                   // tp_version_tag
-};
-
-static int
+int
 RRClass_init(s_RRClass* self, PyObject* args) {
     const char* s;
     long i;
@@ -164,7 +102,7 @@ RRClass_init(s_RRClass* self, PyObject* args) {
     // (the way to do exceptions is to set PyErr and return -1)
     try {
         if (PyArg_ParseTuple(args, "s", &s)) {
-            self->rrclass = new RRClass(s);
+            self->cppobj = new RRClass(s);
             return (0);
         } else if (PyArg_ParseTuple(args, "l", &i)) {
             if (i < 0 || i > 0xffff) {
@@ -173,7 +111,7 @@ RRClass_init(s_RRClass* self, PyObject* args) {
                                 "RR class number out of range");
                 return (-1);
             }
-            self->rrclass = new RRClass(i);
+            self->cppobj = new RRClass(i);
             return (0);
         } else if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) {
             uint8_t data[2];
@@ -182,7 +120,7 @@ RRClass_init(s_RRClass* self, PyObject* args) {
                 return (result);
             }
             InputBuffer ib(data, 2);
-            self->rrclass = new RRClass(ib);
+            self->cppobj = new RRClass(ib);
             PyErr_Clear();
             return (0);
         }
@@ -199,20 +137,20 @@ RRClass_init(s_RRClass* self, PyObject* args) {
     return (-1);
 }
 
-static void
+void
 RRClass_destroy(s_RRClass* self) {
-    delete self->rrclass;
-    self->rrclass = NULL;
+    delete self->cppobj;
+    self->cppobj = NULL;
     Py_TYPE(self)->tp_free(self);
 }
 
-static PyObject*
+PyObject*
 RRClass_toText(s_RRClass* self) {
     // Py_BuildValue makes python objects from native data
-    return (Py_BuildValue("s", self->rrclass->toText().c_str()));
+    return (Py_BuildValue("s", self->cppobj->toText().c_str()));
 }
 
-static PyObject*
+PyObject*
 RRClass_str(PyObject* self) {
     // Simply call the to_text method we already defined
     return (PyObject_CallMethod(self,
@@ -220,16 +158,16 @@ RRClass_str(PyObject* self) {
                                 const_cast<char*>("")));
 }
 
-static PyObject*
+PyObject*
 RRClass_toWire(s_RRClass* self, PyObject* args) {
     PyObject* bytes;
-    s_MessageRenderer* mr;
-    
+    PyObject* mr;
+
     if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) {
         PyObject* bytes_o = bytes;
-        
+
         OutputBuffer buffer(2);
-        self->rrclass->toWire(buffer);
+        self->cppobj->toWire(buffer);
         PyObject* n = PyBytes_FromStringAndSize(static_cast<const char*>(buffer.getData()), buffer.getLength());
         PyObject* result = PySequence_InPlaceConcat(bytes_o, n);
         // We need to release the object we temporarily created here
@@ -237,7 +175,7 @@ RRClass_toWire(s_RRClass* self, PyObject* args) {
         Py_DECREF(n);
         return (result);
     } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
-        self->rrclass->toWire(*mr->messagerenderer);
+        self->cppobj->toWire(PyMessageRenderer_ToMessageRenderer(mr));
         // If we return NULL it is seen as an error, so use this for
         // None returns
         Py_RETURN_NONE;
@@ -248,12 +186,12 @@ RRClass_toWire(s_RRClass* self, PyObject* args) {
     return (NULL);
 }
 
-static PyObject*
+PyObject*
 RRClass_getCode(s_RRClass* self) {
-    return (Py_BuildValue("I", self->rrclass->getCode()));
+    return (Py_BuildValue("I", self->cppobj->getCode()));
 }
 
-static PyObject* 
+PyObject*
 RRClass_richcmp(s_RRClass* self, s_RRClass* other, int op) {
     bool c;
 
@@ -265,24 +203,24 @@ RRClass_richcmp(s_RRClass* self, s_RRClass* other, int op) {
 
     switch (op) {
     case Py_LT:
-        c = *self->rrclass < *other->rrclass;
+        c = *self->cppobj < *other->cppobj;
         break;
     case Py_LE:
-        c = *self->rrclass < *other->rrclass ||
-            *self->rrclass == *other->rrclass;
+        c = *self->cppobj < *other->cppobj ||
+            *self->cppobj == *other->cppobj;
         break;
     case Py_EQ:
-        c = *self->rrclass == *other->rrclass;
+        c = *self->cppobj == *other->cppobj;
         break;
     case Py_NE:
-        c = *self->rrclass != *other->rrclass;
+        c = *self->cppobj != *other->cppobj;
         break;
     case Py_GT:
-        c = *other->rrclass < *self->rrclass;
+        c = *other->cppobj < *self->cppobj;
         break;
     case Py_GE:
-        c = *other->rrclass < *self->rrclass ||
-            *self->rrclass == *other->rrclass;
+        c = *other->cppobj < *self->cppobj ||
+            *self->cppobj == *other->cppobj;
         break;
     default:
         PyErr_SetString(PyExc_IndexError,
@@ -298,56 +236,131 @@ RRClass_richcmp(s_RRClass* self, s_RRClass* other, int op) {
 //
 // Common function for RRClass_IN/CH/etc.
 //
-static PyObject* RRClass_createStatic(RRClass stc) {
+PyObject* RRClass_createStatic(RRClass stc) {
     s_RRClass* ret = PyObject_New(s_RRClass, &rrclass_type);
     if (ret != NULL) {
-        ret->rrclass = new RRClass(stc);
+        ret->cppobj = new RRClass(stc);
     }
     return (ret);
 }
 
-static PyObject* RRClass_IN(s_RRClass*) {
+PyObject* RRClass_IN(s_RRClass*) {
     return (RRClass_createStatic(RRClass::IN()));
 }
 
-static PyObject* RRClass_CH(s_RRClass*) {
+PyObject* RRClass_CH(s_RRClass*) {
     return (RRClass_createStatic(RRClass::CH()));
 }
 
-static PyObject* RRClass_HS(s_RRClass*) {
+PyObject* RRClass_HS(s_RRClass*) {
     return (RRClass_createStatic(RRClass::HS()));
 }
 
-static PyObject* RRClass_NONE(s_RRClass*) {
+PyObject* RRClass_NONE(s_RRClass*) {
     return (RRClass_createStatic(RRClass::NONE()));
 }
 
-static PyObject* RRClass_ANY(s_RRClass*) {
+PyObject* RRClass_ANY(s_RRClass*) {
     return (RRClass_createStatic(RRClass::ANY()));
 }
-// end of RRClass
+
+} // end anonymous namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+
+//
+// Declaration of the custom exceptions
+// Initialization and addition of these go in the initModulePart
+// function in pydnspp.cc
+//
+PyObject* po_InvalidRRClass;
+PyObject* po_IncompleteRRClass;
+
+
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_RRClass
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject rrclass_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "pydnspp.RRClass",
+    sizeof(s_RRClass),                  // tp_basicsize
+    0,                                  // tp_itemsize
+    (destructor)RRClass_destroy,        // tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash
+    NULL,                               // tp_call
+    RRClass_str,                        // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    "The RRClass class encapsulates DNS resource record classes.\n"
+    "This class manages the 16-bit integer class codes in quite a straightforward"
+    "way.  The only non trivial task is to handle textual representations of"
+    "RR classes, such as \"IN\", \"CH\", or \"CLASS65534\".",
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    (richcmpfunc)RRClass_richcmp,       // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    RRClass_methods,                    // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    (initproc)RRClass_init,             // tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
+
+PyObject*
+createRRClassObject(const RRClass& source) {
+    RRClassContainer container(PyObject_New(s_RRClass, &rrclass_type));
+    container.set(new RRClass(source));
+    return (container.release());
+}
 
 
-// Module Initialization, all statics are initialized here
 bool
-initModulePart_RRClass(PyObject* mod) {
-    // Add the exceptions to the module
-    po_InvalidRRClass = PyErr_NewException("pydnspp.InvalidRRClass", NULL, NULL);
-    Py_INCREF(po_InvalidRRClass);
-    PyModule_AddObject(mod, "InvalidRRClass", po_InvalidRRClass);
-    po_IncompleteRRClass = PyErr_NewException("pydnspp.IncompleteRRClass", NULL, NULL);
-    Py_INCREF(po_IncompleteRRClass);
-    PyModule_AddObject(mod, "IncompleteRRClass", po_IncompleteRRClass);
-
-    // We initialize the static description object with PyType_Ready(),
-    // then add it to the module. This is not just a check! (leaving
-    // this out results in segmentation faults)
-    if (PyType_Ready(&rrclass_type) < 0) {
-        return (false);
+PyRRClass_Check(PyObject* obj) {
+    if (obj == NULL) {
+        isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck");
     }
-    Py_INCREF(&rrclass_type);
-    PyModule_AddObject(mod, "RRClass",
-                       reinterpret_cast<PyObject*>(&rrclass_type));
-    
-    return (true);
+    return (PyObject_TypeCheck(obj, &rrclass_type));
 }
+
+const RRClass&
+PyRRClass_ToRRClass(const PyObject* rrclass_obj) {
+    if (rrclass_obj == NULL) {
+        isc_throw(PyCPPWrapperException,
+                  "obj argument NULL in RRClass PyObject conversion");
+    }
+    const s_RRClass* rrclass = static_cast<const s_RRClass*>(rrclass_obj);
+    return (*rrclass->cppobj);
+}
+
+} // end namespace python
+} // end namespace dns
+} // end namespace isc
diff --git a/src/lib/dns/python/rrclass_python.h b/src/lib/dns/python/rrclass_python.h
new file mode 100644
index 0000000..f58bba6
--- /dev/null
+++ b/src/lib/dns/python/rrclass_python.h
@@ -0,0 +1,68 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_RRCLASS_H
+#define __PYTHON_RRCLASS_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace dns {
+class RRClass;
+
+namespace python {
+
+extern PyObject* po_InvalidRRClass;
+extern PyObject* po_IncompleteRRClass;
+
+extern PyTypeObject rrclass_type;
+
+/// This is a simple shortcut to create a python RRClass object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called within a try block
+/// followed by necessary setup for python exception.
+PyObject* createRRClassObject(const RRClass& source);
+
+/// \brief Checks if the given python object is a RRClass object
+///
+/// \exception PyCPPWrapperException if obj is NULL
+///
+/// \param obj The object to check the type of
+/// \return true if the object is of type RRClass, false otherwise
+bool PyRRClass_Check(PyObject* obj);
+
+/// \brief Returns a reference to the RRClass object contained within the given
+///        Python object.
+///
+/// \note The given object MUST be of type RRClass; this can be checked with
+///       either the right call to ParseTuple("O!"), or with PyRRClass_Check()
+///
+/// \note This is not a copy; if the RRClass is needed when the PyObject
+/// may be destroyed, the caller must copy it itself.
+///
+/// \param rrclass_obj The rrclass object to convert
+const RRClass& PyRRClass_ToRRClass(const PyObject* rrclass_obj);
+
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_RRCLASS_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/rrset_python.cc b/src/lib/dns/python/rrset_python.cc
index 71a0710..9fc3d79 100644
--- a/src/lib/dns/python/rrset_python.cc
+++ b/src/lib/dns/python/rrset_python.cc
@@ -12,55 +12,63 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <dns/rrset.h>
+#include <Python.h>
 
-//
-// Declaration of the custom exceptions
-// Initialization and addition of these go in the module init at the
-// end
-//
-static PyObject* po_EmptyRRset;
+#include <util/python/pycppwrapper_util.h>
 
-//
-// Definition of the classes
-//
-
-// For each class, we need a struct, a helper functions (init, destroy,
-// and static wrappers around the methods we export), a list of methods,
-// and a type description
+#include <dns/rrset.h>
+#include <dns/name.h>
+#include <dns/messagerenderer.h>
+
+#include "name_python.h"
+#include "pydnspp_common.h"
+#include "rrset_python.h"
+#include "rrclass_python.h"
+#include "rrtype_python.h"
+#include "rrttl_python.h"
+#include "rdata_python.h"
+#include "messagerenderer_python.h"
+
+using namespace std;
 using namespace isc::dns;
+using namespace isc::dns::python;
 using namespace isc::util;
+using namespace isc::util::python;
+
+namespace {
 
-// RRset
+// The s_* Class simply coverst one instantiation of the object
 
 // Using a shared_ptr here should not really be necessary (PyObject
 // is already reference-counted), however internally on the cpp side,
 // not doing so might result in problems, since we can't copy construct
-// rrsets, adding them to messages results in a problem when the
-// message is destroyed or cleared later
+// rdata field, adding them to rrsets results in a problem when the
+// rrset is destroyed later
 class s_RRset : public PyObject {
 public:
-    RRsetPtr rrset;
+    isc::dns::RRsetPtr cppobj;
 };
 
-static int RRset_init(s_RRset* self, PyObject* args);
-static void RRset_destroy(s_RRset* self);
-
-static PyObject* RRset_getRdataCount(s_RRset* self);
-static PyObject* RRset_getName(s_RRset* self);
-static PyObject* RRset_getClass(s_RRset* self);
-static PyObject* RRset_getType(s_RRset* self);
-static PyObject* RRset_getTTL(s_RRset* self);
-static PyObject* RRset_setName(s_RRset* self, PyObject* args);
-static PyObject* RRset_setTTL(s_RRset* self, PyObject* args);
-static PyObject* RRset_toText(s_RRset* self);
-static PyObject* RRset_str(PyObject* self);
-static PyObject* RRset_toWire(s_RRset* self, PyObject* args);
-static PyObject* RRset_addRdata(s_RRset* self, PyObject* args);
-static PyObject* RRset_getRdata(s_RRset* self);
+int RRset_init(s_RRset* self, PyObject* args);
+void RRset_destroy(s_RRset* self);
+
+PyObject* RRset_getRdataCount(s_RRset* self);
+PyObject* RRset_getName(s_RRset* self);
+PyObject* RRset_getClass(s_RRset* self);
+PyObject* RRset_getType(s_RRset* self);
+PyObject* RRset_getTTL(s_RRset* self);
+PyObject* RRset_setName(s_RRset* self, PyObject* args);
+PyObject* RRset_setTTL(s_RRset* self, PyObject* args);
+PyObject* RRset_toText(s_RRset* self);
+PyObject* RRset_str(PyObject* self);
+PyObject* RRset_toWire(s_RRset* self, PyObject* args);
+PyObject* RRset_addRdata(s_RRset* self, PyObject* args);
+PyObject* RRset_getRdata(s_RRset* self);
+PyObject* RRset_removeRRsig(s_RRset* self);
+
 // TODO: iterator?
 
-static PyMethodDef RRset_methods[] = {
+PyMethodDef RRset_methods[] = {
     { "get_rdata_count", reinterpret_cast<PyCFunction>(RRset_getRdataCount), METH_NOARGS,
       "Returns the number of rdata fields." },
     { "get_name", reinterpret_cast<PyCFunction>(RRset_getName), METH_NOARGS,
@@ -88,208 +96,142 @@ static PyMethodDef RRset_methods[] = {
       "Adds the rdata for one RR to the RRset.\nTakes an Rdata object as an argument" },
     { "get_rdata", reinterpret_cast<PyCFunction>(RRset_getRdata), METH_NOARGS,
       "Returns a List containing all Rdata elements" },
+    { "remove_rrsig", reinterpret_cast<PyCFunction>(RRset_removeRRsig), METH_NOARGS,
+      "Clears the list of RRsigs for this RRset" },
     { NULL, NULL, 0, NULL }
 };
 
-static PyTypeObject rrset_type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "pydnspp.RRset",
-    sizeof(s_RRset),                    // tp_basicsize
-    0,                                  // tp_itemsize
-    (destructor)RRset_destroy,          // tp_dealloc
-    NULL,                               // tp_print
-    NULL,                               // tp_getattr
-    NULL,                               // tp_setattr
-    NULL,                               // tp_reserved
-    NULL,                               // tp_repr
-    NULL,                               // tp_as_number
-    NULL,                               // tp_as_sequence
-    NULL,                               // tp_as_mapping
-    NULL,                               // tp_hash 
-    NULL,                               // tp_call
-    RRset_str,                          // tp_str
-    NULL,                               // tp_getattro
-    NULL,                               // tp_setattro
-    NULL,                               // tp_as_buffer
-    Py_TPFLAGS_DEFAULT,                 // tp_flags
-    "The AbstractRRset class is an abstract base class that "
-    "models a DNS RRset.\n\n"
-    "An object of (a specific derived class of) AbstractRRset "
-    "models an RRset as described in the DNS standard:\n"
-    "A set of DNS resource records (RRs) of the same type and class. "
-    "The standard requires the TTL of all RRs in an RRset be the same; "
-    "this class follows that requirement.\n\n"
-    "Note about duplicate RDATA: RFC2181 states that it's meaningless that an "
-    "RRset contains two identical RRs and that name servers should suppress "
-    "such duplicates.\n"
-    "This class is not responsible for ensuring this requirement: For example, "
-    "addRdata() method doesn't check if there's already RDATA identical "
-    "to the one being added.\n"
-    "This is because such checks can be expensive, and it's often easy to "
-    "ensure the uniqueness requirement at the %data preparation phase "
-    "(e.g. when loading a zone).",
-    NULL,                               // tp_traverse
-    NULL,                               // tp_clear
-    NULL,                               // tp_richcompare
-    0,                                  // tp_weaklistoffset
-    NULL,                               // tp_iter
-    NULL,                               // tp_iternext
-    RRset_methods,                      // tp_methods
-    NULL,                               // tp_members
-    NULL,                               // tp_getset
-    NULL,                               // tp_base
-    NULL,                               // tp_dict
-    NULL,                               // tp_descr_get
-    NULL,                               // tp_descr_set
-    0,                                  // tp_dictoffset
-    (initproc)RRset_init,               // tp_init
-    NULL,                               // tp_alloc
-    PyType_GenericNew,                  // tp_new
-    NULL,                               // tp_free
-    NULL,                               // tp_is_gc
-    NULL,                               // tp_bases
-    NULL,                               // tp_mro
-    NULL,                               // tp_cache
-    NULL,                               // tp_subclasses
-    NULL,                               // tp_weaklist
-    NULL,                               // tp_del
-    0                                   // tp_version_tag
-};
-
-static int
+int
 RRset_init(s_RRset* self, PyObject* args) {
-    s_Name* name;
-    s_RRClass* rrclass;
-    s_RRType* rrtype;
-    s_RRTTL* rrttl;
+    PyObject* name;
+    PyObject* rrclass;
+    PyObject* rrtype;
+    PyObject* rrttl;
 
     if (PyArg_ParseTuple(args, "O!O!O!O!", &name_type, &name,
                                            &rrclass_type, &rrclass,
                                            &rrtype_type, &rrtype,
                                            &rrttl_type, &rrttl
        )) {
-        self->rrset = RRsetPtr(new RRset(*name->cppobj, *rrclass->rrclass,
-                                *rrtype->rrtype, *rrttl->rrttl));
+        self->cppobj = RRsetPtr(new RRset(PyName_ToName(name),
+                                          PyRRClass_ToRRClass(rrclass),
+                                          PyRRType_ToRRType(rrtype),
+                                          PyRRTTL_ToRRTTL(rrttl)));
         return (0);
     }
 
-    self->rrset = RRsetPtr();
+    self->cppobj = RRsetPtr();
     return (-1);
 }
 
-static void
+void
 RRset_destroy(s_RRset* self) {
     // Clear the shared_ptr so that its reference count is zero
     // before we call tp_free() (there is no direct release())
-    self->rrset.reset();
+    self->cppobj.reset();
     Py_TYPE(self)->tp_free(self);
 }
 
-static PyObject*
+PyObject*
 RRset_getRdataCount(s_RRset* self) {
-    return (Py_BuildValue("I", self->rrset->getRdataCount()));
+    return (Py_BuildValue("I", self->cppobj->getRdataCount()));
 }
 
-static PyObject*
+PyObject*
 RRset_getName(s_RRset* self) {
-    s_Name* name;
-
-    // is this the best way to do this?
-    name = static_cast<s_Name*>(name_type.tp_alloc(&name_type, 0));
-    if (name != NULL) {
-        name->cppobj = new Name(self->rrset->getName());
-        if (name->cppobj == NULL)
-          {
-            Py_DECREF(name);
-            return (NULL);
-          }
+    try {
+        return (createNameObject(self->cppobj->getName()));
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Unexpected failure getting rrset Name: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure getting rrset Name");
     }
-
-    return (name);
+    return (NULL);
 }
 
-static PyObject*
+PyObject*
 RRset_getClass(s_RRset* self) {
-    s_RRClass* rrclass;
-
-    rrclass = static_cast<s_RRClass*>(rrclass_type.tp_alloc(&rrclass_type, 0));
-    if (rrclass != NULL) {
-        rrclass->rrclass = new RRClass(self->rrset->getClass());
-        if (rrclass->rrclass == NULL)
-          {
-            Py_DECREF(rrclass);
-            return (NULL);
-          }
+    try {
+        return (createRRClassObject(self->cppobj->getClass()));
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Unexpected failure getting question RRClass: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure getting question RRClass");
     }
-
-    return (rrclass);
+    return (NULL);
 }
 
-static PyObject*
+PyObject*
 RRset_getType(s_RRset* self) {
-    s_RRType* rrtype;
-
-    rrtype = static_cast<s_RRType*>(rrtype_type.tp_alloc(&rrtype_type, 0));
-    if (rrtype != NULL) {
-        rrtype->rrtype = new RRType(self->rrset->getType());
-        if (rrtype->rrtype == NULL)
-          {
-            Py_DECREF(rrtype);
-            return (NULL);
-          }
+    try {
+        return (createRRTypeObject(self->cppobj->getType()));
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Unexpected failure getting question RRType: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure getting question RRType");
     }
-
-    return (rrtype);
+    return (NULL);
 }
 
-static PyObject*
+PyObject*
 RRset_getTTL(s_RRset* self) {
-    s_RRTTL* rrttl;
-
-    rrttl = static_cast<s_RRTTL*>(rrttl_type.tp_alloc(&rrttl_type, 0));
-    if (rrttl != NULL) {
-        rrttl->rrttl = new RRTTL(self->rrset->getTTL());
-        if (rrttl->rrttl == NULL)
-          {
-            Py_DECREF(rrttl);
-            return (NULL);
-          }
+    try {
+        return (createRRTTLObject(self->cppobj->getTTL()));
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Unexpected failure getting question TTL: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure getting question TTL");
     }
-
-    return (rrttl);
+    return (NULL);
 }
 
-static PyObject*
+PyObject*
 RRset_setName(s_RRset* self, PyObject* args) {
-    s_Name* name;
+    PyObject* name;
     if (!PyArg_ParseTuple(args, "O!", &name_type, &name)) {
         return (NULL);
     }
-    self->rrset->setName(*name->cppobj);
+    self->cppobj->setName(PyName_ToName(name));
     Py_RETURN_NONE;
 }
 
-static PyObject*
+PyObject*
 RRset_setTTL(s_RRset* self, PyObject* args) {
-    s_RRTTL* rrttl;
+    PyObject* rrttl;
     if (!PyArg_ParseTuple(args, "O!", &rrttl_type, &rrttl)) {
         return (NULL);
     }
-    self->rrset->setTTL(*rrttl->rrttl);
+    self->cppobj->setTTL(PyRRTTL_ToRRTTL(rrttl));
     Py_RETURN_NONE;
 }
 
-static PyObject*
+PyObject*
 RRset_toText(s_RRset* self) {
     try {
-        return (Py_BuildValue("s", self->rrset->toText().c_str()));
+        return (Py_BuildValue("s", self->cppobj->toText().c_str()));
     } catch (const EmptyRRset& ers) {
         PyErr_SetString(po_EmptyRRset, ers.what());
         return (NULL);
     }
 }
 
-static PyObject*
+PyObject*
 RRset_str(PyObject* self) {
     // Simply call the to_text method we already defined
     return (PyObject_CallMethod(self,
@@ -297,17 +239,17 @@ RRset_str(PyObject* self) {
                                 const_cast<char*>("")));
 }
 
-static PyObject*
+PyObject*
 RRset_toWire(s_RRset* self, PyObject* args) {
     PyObject* bytes;
-    s_MessageRenderer* mr;
+    PyObject* mr;
 
     try {
         if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) {
             PyObject* bytes_o = bytes;
-            
+
             OutputBuffer buffer(4096);
-            self->rrset->toWire(buffer);
+            self->cppobj->toWire(buffer);
             PyObject* n = PyBytes_FromStringAndSize(static_cast<const char*>(buffer.getData()), buffer.getLength());
             PyObject* result = PySequence_InPlaceConcat(bytes_o, n);
             // We need to release the object we temporarily created here
@@ -315,7 +257,7 @@ RRset_toWire(s_RRset* self, PyObject* args) {
             Py_DECREF(n);
             return (result);
         } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
-            self->rrset->toWire(*mr->messagerenderer);
+            self->cppobj->toWire(PyMessageRenderer_ToMessageRenderer(mr));
             // If we return NULL it is seen as an error, so use this for
             // None returns
             Py_RETURN_NONE;
@@ -331,14 +273,14 @@ RRset_toWire(s_RRset* self, PyObject* args) {
     return (NULL);
 }
 
-static PyObject*
+PyObject*
 RRset_addRdata(s_RRset* self, PyObject* args) {
-    s_Rdata* rdata;
+    PyObject* rdata;
     if (!PyArg_ParseTuple(args, "O!", &rdata_type, &rdata)) {
         return (NULL);
     }
     try {
-        self->rrset->addRdata(*rdata->rdata);
+        self->cppobj->addRdata(PyRdata_ToRdata(rdata));
         Py_RETURN_NONE;
     } catch (const std::bad_cast&) {
         PyErr_Clear();
@@ -348,55 +290,173 @@ RRset_addRdata(s_RRset* self, PyObject* args) {
     }
 }
 
-static PyObject*
+PyObject*
 RRset_getRdata(s_RRset* self) {
     PyObject* list = PyList_New(0);
 
-    RdataIteratorPtr it = self->rrset->getRdataIterator();
-
-    for (; !it->isLast(); it->next()) {
-        s_Rdata *rds = static_cast<s_Rdata*>(rdata_type.tp_alloc(&rdata_type, 0));
-        if (rds != NULL) {
-            // hmz them iterators/shared_ptrs and private constructors
-            // make this a bit weird, so we create a new one with
-            // the data available
-            const Rdata *rd = &it->getCurrent();
-            rds->rdata = createRdata(self->rrset->getType(), self->rrset->getClass(), *rd);
-            PyList_Append(list, rds);
-        } else {
-            return (NULL);
+    RdataIteratorPtr it = self->cppobj->getRdataIterator();
+
+    try {
+        for (; !it->isLast(); it->next()) {
+            const rdata::Rdata *rd = &it->getCurrent();
+            if (PyList_Append(list,
+                    createRdataObject(createRdata(self->cppobj->getType(),
+                                      self->cppobj->getClass(), *rd))) == -1) {
+                Py_DECREF(list);
+                return (NULL);
+            }
         }
+        return (list);
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Unexpected failure getting rrset Rdata: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure getting rrset Rdata");
     }
-    
-    return (list);
+    Py_DECREF(list);
+    return (NULL);
 }
 
-// end of RRset
+PyObject*
+RRset_removeRRsig(s_RRset* self) {
+    self->cppobj->removeRRsig();
+    Py_RETURN_NONE;
+}
 
+} // end of unnamed namespace
 
-// Module Initialization, all statics are initialized here
-bool
-initModulePart_RRset(PyObject* mod) {
-    // Add the exceptions to the module
-    po_EmptyRRset = PyErr_NewException("pydnspp.EmptyRRset", NULL, NULL);
-    PyModule_AddObject(mod, "EmptyRRset", po_EmptyRRset);
+namespace isc {
+namespace dns {
+namespace python {
 
-    // Add the enums to the module
+//
+// Declaration of the custom exceptions
+// Initialization and addition of these go in the module init at the
+// end
+//
+PyObject* po_EmptyRRset;
 
-    // Add the constants to the module
+PyTypeObject rrset_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "pydnspp.RRset",
+    sizeof(s_RRset),                    // tp_basicsize
+    0,                                  // tp_itemsize
+    (destructor)RRset_destroy,          // tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash
+    NULL,                               // tp_call
+    RRset_str,                          // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    "The AbstractRRset class is an abstract base class that "
+    "models a DNS RRset.\n\n"
+    "An object of (a specific derived class of) AbstractRRset "
+    "models an RRset as described in the DNS standard:\n"
+    "A set of DNS resource records (RRs) of the same type and class. "
+    "The standard requires the TTL of all RRs in an RRset be the same; "
+    "this class follows that requirement.\n\n"
+    "Note about duplicate RDATA: RFC2181 states that it's meaningless that an "
+    "RRset contains two identical RRs and that name servers should suppress "
+    "such duplicates.\n"
+    "This class is not responsible for ensuring this requirement: For example, "
+    "addRdata() method doesn't check if there's already RDATA identical "
+    "to the one being added.\n"
+    "This is because such checks can be expensive, and it's often easy to "
+    "ensure the uniqueness requirement at the %data preparation phase "
+    "(e.g. when loading a zone).",
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    NULL,                               // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    RRset_methods,                      // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    (initproc)RRset_init,               // tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
+
+PyObject*
+createRRsetObject(const RRset& source) {
 
-    // Add the classes to the module
-    // We initialize the static description object with PyType_Ready(),
-    // then add it to the module
+    // RRsets are noncopyable, so as a workaround we recreate a new one
+    // and copy over all content
+    RRsetPtr new_rrset = isc::dns::RRsetPtr(
+        new isc::dns::RRset(source.getName(), source.getClass(),
+                            source.getType(), source.getTTL()));
 
-    // NameComparisonResult
-    if (PyType_Ready(&rrset_type) < 0) {
-        return (false);
+    isc::dns::RdataIteratorPtr rdata_it(source.getRdataIterator());
+    for (rdata_it->first(); !rdata_it->isLast(); rdata_it->next()) {
+        new_rrset->addRdata(rdata_it->getCurrent());
+    }
+
+    isc::dns::RRsetPtr sigs = source.getRRsig();
+    if (sigs) {
+        new_rrset->addRRsig(sigs);
+    }
+    s_RRset* py_rrset =
+        static_cast<s_RRset*>(rrset_type.tp_alloc(&rrset_type, 0));
+    if (py_rrset == NULL) {
+        isc_throw(PyCPPWrapperException, "Unexpected NULL C++ object, "
+                  "probably due to short memory");
     }
-    Py_INCREF(&rrset_type);
-    PyModule_AddObject(mod, "RRset",
-                       reinterpret_cast<PyObject*>(&rrset_type));
-    
-    return (true);
+    py_rrset->cppobj = new_rrset;
+    return (py_rrset);
 }
 
+bool
+PyRRset_Check(PyObject* obj) {
+    if (obj == NULL) {
+        isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck");
+    }
+    return (PyObject_TypeCheck(obj, &rrset_type));
+}
+
+RRset&
+PyRRset_ToRRset(PyObject* rrset_obj) {
+    s_RRset* rrset = static_cast<s_RRset*>(rrset_obj);
+    return (*rrset->cppobj);
+}
+
+RRsetPtr
+PyRRset_ToRRsetPtr(PyObject* rrset_obj) {
+    if (rrset_obj == NULL) {
+        isc_throw(PyCPPWrapperException,
+                  "obj argument NULL in RRset PyObject conversion");
+    }
+    s_RRset* rrset = static_cast<s_RRset*>(rrset_obj);
+    return (rrset->cppobj);
+}
+
+
+} // end python namespace
+} // end dns namespace
+} // end isc namespace
diff --git a/src/lib/dns/python/rrset_python.h b/src/lib/dns/python/rrset_python.h
new file mode 100644
index 0000000..4268678
--- /dev/null
+++ b/src/lib/dns/python/rrset_python.h
@@ -0,0 +1,78 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_RRSET_H
+#define __PYTHON_RRSET_H 1
+
+#include <Python.h>
+
+#include <dns/rrset.h>
+
+#include <util/python/pycppwrapper_util.h>
+
+namespace isc {
+namespace dns {
+namespace python {
+
+extern PyObject* po_EmptyRRset;
+
+extern PyTypeObject rrset_type;
+
+/// This is a simple shortcut to create a python RRset object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called within a try block
+/// followed by necessary setup for python exception.
+PyObject* createRRsetObject(const RRset& source);
+
+/// \brief Checks if the given python object is a RRset object
+///
+/// \exception PyCPPWrapperException if obj is NULL
+///
+/// \param obj The object to check the type of
+/// \return true if the object is of type RRset, false otherwise
+bool PyRRset_Check(PyObject* obj);
+
+/// \brief Returns a reference to the RRset object contained within the given
+///        Python object.
+///
+/// \note The given object MUST be of type RRset; this can be checked with
+///       either the right call to ParseTuple("O!"), or with PyRRset_Check()
+///
+/// \note This is not a copy; if the RRset is needed when the PyObject
+/// may be destroyed, the caller must copy it itself.
+///
+/// \param rrset_obj The rrset object to convert
+RRset& PyRRset_ToRRset(PyObject* rrset_obj);
+
+/// \brief Returns the shared_ptr of the RRset object contained within the
+///        given Python object.
+///
+/// \note The given object MUST be of type RRset; this can be checked with
+///       either the right call to ParseTuple("O!"), or with PyRRset_Check()
+///
+/// \param rrset_obj The rrset object to convert
+RRsetPtr PyRRset_ToRRsetPtr(PyObject* rrset_obj);
+
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_RRSET_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/rrttl_python.cc b/src/lib/dns/python/rrttl_python.cc
index c4b25bf..3a3f067 100644
--- a/src/lib/dns/python/rrttl_python.cc
+++ b/src/lib/dns/python/rrttl_python.cc
@@ -12,57 +12,41 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <Python.h>
 #include <vector>
 
 #include <dns/rrttl.h>
+#include <dns/messagerenderer.h>
+#include <util/buffer.h>
+#include <util/python/pycppwrapper_util.h>
+
+#include "rrttl_python.h"
+#include "pydnspp_common.h"
+#include "messagerenderer_python.h"
 
 using namespace std;
 using namespace isc::dns;
+using namespace isc::dns::python;
 using namespace isc::util;
+using namespace isc::util::python;
 
-//
-// Declaration of the custom exceptions
-// Initialization and addition of these go in the initModulePart
-// function at the end of this file
-//
-static PyObject* po_InvalidRRTTL;
-static PyObject* po_IncompleteRRTTL;
-
-//
-// Definition of the classes
-//
-
-// For each class, we need a struct, a helper functions (init, destroy,
-// and static wrappers around the methods we export), a list of methods,
-// and a type description
-
-//
-// RRTTL
-//
-
+namespace {
 // The s_* Class simply covers one instantiation of the object
 class s_RRTTL : public PyObject {
 public:
-    RRTTL* rrttl;
+    s_RRTTL() : cppobj(NULL) {};
+    isc::dns::RRTTL* cppobj;
 };
 
-//
-// We declare the functions here, the definitions are below
-// the type definition of the object, since both can use the other
-//
-
-// General creation and destruction
-static int RRTTL_init(s_RRTTL* self, PyObject* args);
-static void RRTTL_destroy(s_RRTTL* self);
+typedef CPPPyObjectContainer<s_RRTTL, RRTTL> RRTTLContainer;
 
-// These are the functions we export
-static PyObject* RRTTL_toText(s_RRTTL* self);
+PyObject* RRTTL_toText(s_RRTTL* self);
 // This is a second version of toText, we need one where the argument
 // is a PyObject*, for the str() function in python.
-static PyObject* RRTTL_str(PyObject* self);
-static PyObject* RRTTL_toWire(s_RRTTL* self, PyObject* args);
-static PyObject* RRTTL_getValue(s_RRTTL* self);
-static PyObject* RRTTL_richcmp(s_RRTTL* self, s_RRTTL* other, int op);
+PyObject* RRTTL_str(PyObject* self);
+PyObject* RRTTL_toWire(s_RRTTL* self, PyObject* args);
+PyObject* RRTTL_getValue(s_RRTTL* self);
+PyObject* RRTTL_richcmp(s_RRTTL* self, s_RRTTL* other, int op);
 
 // This list contains the actual set of functions we have in
 // python. Each entry has
@@ -70,7 +54,7 @@ static PyObject* RRTTL_richcmp(s_RRTTL* self, s_RRTTL* other, int op);
 // 2. Our static function here
 // 3. Argument type
 // 4. Documentation
-static PyMethodDef RRTTL_methods[] = {
+PyMethodDef RRTTL_methods[] = {
     { "to_text", reinterpret_cast<PyCFunction>(RRTTL_toText), METH_NOARGS,
       "Returns the string representation" },
     { "to_wire", reinterpret_cast<PyCFunction>(RRTTL_toWire), METH_VARARGS,
@@ -85,65 +69,7 @@ static PyMethodDef RRTTL_methods[] = {
     { NULL, NULL, 0, NULL }
 };
 
-// This defines the complete type for reflection in python and
-// parsing of PyObject* to s_RRTTL
-// Most of the functions are not actually implemented and NULL here.
-static PyTypeObject rrttl_type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "pydnspp.RRTTL",
-    sizeof(s_RRTTL),                    // tp_basicsize
-    0,                                  // tp_itemsize
-    (destructor)RRTTL_destroy,          // tp_dealloc
-    NULL,                               // tp_print
-    NULL,                               // tp_getattr
-    NULL,                               // tp_setattr
-    NULL,                               // tp_reserved
-    NULL,                               // tp_repr
-    NULL,                               // tp_as_number
-    NULL,                               // tp_as_sequence
-    NULL,                               // tp_as_mapping
-    NULL,                               // tp_hash 
-    NULL,                               // tp_call
-    RRTTL_str,                          // tp_str
-    NULL,                               // tp_getattro
-    NULL,                               // tp_setattro
-    NULL,                               // tp_as_buffer
-    Py_TPFLAGS_DEFAULT,                 // tp_flags
-    "The RRTTL class encapsulates TTLs used in DNS resource records.\n\n"
-    "This is a straightforward class; an RRTTL object simply maintains a "
-    "32-bit unsigned integer corresponding to the TTL value.  The main purpose "
-    "of this class is to provide convenient interfaces to convert a textual "
-    "representation into the integer TTL value and vice versa, and to handle "
-    "wire-format representations.",
-    NULL,                               // tp_traverse
-    NULL,                               // tp_clear
-    (richcmpfunc)RRTTL_richcmp,         // tp_richcompare
-    0,                                  // tp_weaklistoffset
-    NULL,                               // tp_iter
-    NULL,                               // tp_iternext
-    RRTTL_methods,                      // tp_methods
-    NULL,                               // tp_members
-    NULL,                               // tp_getset
-    NULL,                               // tp_base
-    NULL,                               // tp_dict
-    NULL,                               // tp_descr_get
-    NULL,                               // tp_descr_set
-    0,                                  // tp_dictoffset
-    (initproc)RRTTL_init,               // tp_init
-    NULL,                               // tp_alloc
-    PyType_GenericNew,                  // tp_new
-    NULL,                               // tp_free
-    NULL,                               // tp_is_gc
-    NULL,                               // tp_bases
-    NULL,                               // tp_mro
-    NULL,                               // tp_cache
-    NULL,                               // tp_subclasses
-    NULL,                               // tp_weaklist
-    NULL,                               // tp_del
-    0                                   // tp_version_tag
-};
-
-static int
+int
 RRTTL_init(s_RRTTL* self, PyObject* args) {
     const char* s;
     long long i;
@@ -157,7 +83,7 @@ RRTTL_init(s_RRTTL* self, PyObject* args) {
     // (the way to do exceptions is to set PyErr and return -1)
     try {
         if (PyArg_ParseTuple(args, "s", &s)) {
-            self->rrttl = new RRTTL(s);
+            self->cppobj = new RRTTL(s);
             return (0);
         } else if (PyArg_ParseTuple(args, "L", &i)) {
             PyErr_Clear();
@@ -165,7 +91,7 @@ RRTTL_init(s_RRTTL* self, PyObject* args) {
                 PyErr_SetString(PyExc_ValueError, "RR TTL number out of range");
                 return (-1);
             }
-            self->rrttl = new RRTTL(i);
+            self->cppobj = new RRTTL(i);
             return (0);
         } else if (PyArg_ParseTuple(args, "O", &bytes) &&
                    PySequence_Check(bytes)) {
@@ -176,7 +102,7 @@ RRTTL_init(s_RRTTL* self, PyObject* args) {
                 return (result);
             }
             InputBuffer ib(&data[0], size);
-            self->rrttl = new RRTTL(ib);
+            self->cppobj = new RRTTL(ib);
             PyErr_Clear();
             return (0);
         }
@@ -200,20 +126,20 @@ RRTTL_init(s_RRTTL* self, PyObject* args) {
     return (-1);
 }
 
-static void
+void
 RRTTL_destroy(s_RRTTL* self) {
-    delete self->rrttl;
-    self->rrttl = NULL;
+    delete self->cppobj;
+    self->cppobj = NULL;
     Py_TYPE(self)->tp_free(self);
 }
 
-static PyObject*
+PyObject*
 RRTTL_toText(s_RRTTL* self) {
     // Py_BuildValue makes python objects from native data
-    return (Py_BuildValue("s", self->rrttl->toText().c_str()));
+    return (Py_BuildValue("s", self->cppobj->toText().c_str()));
 }
 
-static PyObject*
+PyObject*
 RRTTL_str(PyObject* self) {
     // Simply call the to_text method we already defined
     return (PyObject_CallMethod(self,
@@ -221,16 +147,16 @@ RRTTL_str(PyObject* self) {
                                 const_cast<char*>("")));
 }
 
-static PyObject*
+PyObject*
 RRTTL_toWire(s_RRTTL* self, PyObject* args) {
     PyObject* bytes;
-    s_MessageRenderer* mr;
-    
+    PyObject* mr;
+
     if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) {
         PyObject* bytes_o = bytes;
-        
+
         OutputBuffer buffer(4);
-        self->rrttl->toWire(buffer);
+        self->cppobj->toWire(buffer);
         PyObject* n = PyBytes_FromStringAndSize(static_cast<const char*>(buffer.getData()),
                                                 buffer.getLength());
         PyObject* result = PySequence_InPlaceConcat(bytes_o, n);
@@ -239,7 +165,7 @@ RRTTL_toWire(s_RRTTL* self, PyObject* args) {
         Py_DECREF(n);
         return (result);
     } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
-        self->rrttl->toWire(*mr->messagerenderer);
+        self->cppobj->toWire(PyMessageRenderer_ToMessageRenderer(mr));
         // If we return NULL it is seen as an error, so use this for
         // None returns
         Py_RETURN_NONE;
@@ -250,12 +176,12 @@ RRTTL_toWire(s_RRTTL* self, PyObject* args) {
     return (NULL);
 }
 
-static PyObject*
+PyObject*
 RRTTL_getValue(s_RRTTL* self) {
-    return (Py_BuildValue("I", self->rrttl->getValue()));
+    return (Py_BuildValue("I", self->cppobj->getValue()));
 }
 
-static PyObject* 
+PyObject*
 RRTTL_richcmp(s_RRTTL* self, s_RRTTL* other, int op) {
     bool c = false;
 
@@ -267,24 +193,24 @@ RRTTL_richcmp(s_RRTTL* self, s_RRTTL* other, int op) {
 
     switch (op) {
     case Py_LT:
-        c = *self->rrttl < *other->rrttl;
+        c = *self->cppobj < *other->cppobj;
         break;
     case Py_LE:
-        c = *self->rrttl < *other->rrttl ||
-            *self->rrttl == *other->rrttl;
+        c = *self->cppobj < *other->cppobj ||
+            *self->cppobj == *other->cppobj;
         break;
     case Py_EQ:
-        c = *self->rrttl == *other->rrttl;
+        c = *self->cppobj == *other->cppobj;
         break;
     case Py_NE:
-        c = *self->rrttl != *other->rrttl;
+        c = *self->cppobj != *other->cppobj;
         break;
     case Py_GT:
-        c = *other->rrttl < *self->rrttl;
+        c = *other->cppobj < *self->cppobj;
         break;
     case Py_GE:
-        c = *other->rrttl < *self->rrttl ||
-            *self->rrttl == *other->rrttl;
+        c = *other->cppobj < *self->cppobj ||
+            *self->cppobj == *other->cppobj;
         break;
     }
     if (c)
@@ -292,27 +218,104 @@ RRTTL_richcmp(s_RRTTL* self, s_RRTTL* other, int op) {
     else
         Py_RETURN_FALSE;
 }
-// end of RRTTL
 
+} // end anonymous namespace
+
+namespace isc {
+namespace dns {
+namespace python {
+
+//
+// Declaration of the custom exceptions
+// Initialization and addition of these go in the initModulePart
+// function in pydnspp.cc
+//
+PyObject* po_InvalidRRTTL;
+PyObject* po_IncompleteRRTTL;
+
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_RRTTL
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject rrttl_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "pydnspp.RRTTL",
+    sizeof(s_RRTTL),                    // tp_basicsize
+    0,                                  // tp_itemsize
+    (destructor)RRTTL_destroy,          // tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash
+    NULL,                               // tp_call
+    RRTTL_str,                          // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    "The RRTTL class encapsulates TTLs used in DNS resource records.\n\n"
+    "This is a straightforward class; an RRTTL object simply maintains a "
+    "32-bit unsigned integer corresponding to the TTL value.  The main purpose "
+    "of this class is to provide convenient interfaces to convert a textual "
+    "representation into the integer TTL value and vice versa, and to handle "
+    "wire-format representations.",
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    (richcmpfunc)RRTTL_richcmp,         // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    RRTTL_methods,                      // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    (initproc)RRTTL_init,               // tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
+
+PyObject*
+createRRTTLObject(const RRTTL& source) {
+    RRTTLContainer container(PyObject_New(s_RRTTL, &rrttl_type));
+    container.set(new RRTTL(source));
+    return (container.release());
+}
 
-// Module Initialization, all statics are initialized here
 bool
-initModulePart_RRTTL(PyObject* mod) {
-    // Add the exceptions to the module
-    po_InvalidRRTTL = PyErr_NewException("pydnspp.InvalidRRTTL", NULL, NULL);
-    PyModule_AddObject(mod, "InvalidRRTTL", po_InvalidRRTTL);
-    po_IncompleteRRTTL = PyErr_NewException("pydnspp.IncompleteRRTTL", NULL, NULL);
-    PyModule_AddObject(mod, "IncompleteRRTTL", po_IncompleteRRTTL);
+PyRRTTL_Check(PyObject* obj) {
+    if (obj == NULL) {
+        isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck");
+    }
+    return (PyObject_TypeCheck(obj, &rrttl_type));
+}
 
-    // We initialize the static description object with PyType_Ready(),
-    // then add it to the module. This is not just a check! (leaving
-    // this out results in segmentation faults)
-    if (PyType_Ready(&rrttl_type) < 0) {
-        return (false);
+const RRTTL&
+PyRRTTL_ToRRTTL(const PyObject* rrttl_obj) {
+    if (rrttl_obj == NULL) {
+        isc_throw(PyCPPWrapperException,
+                  "obj argument NULL in RRTTL PyObject conversion");
     }
-    Py_INCREF(&rrttl_type);
-    PyModule_AddObject(mod, "RRTTL",
-                       reinterpret_cast<PyObject*>(&rrttl_type));
-    
-    return (true);
+    const s_RRTTL* rrttl = static_cast<const s_RRTTL*>(rrttl_obj);
+    return (*rrttl->cppobj);
 }
+
+} // namespace python
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/python/rrttl_python.h b/src/lib/dns/python/rrttl_python.h
new file mode 100644
index 0000000..9dbc982
--- /dev/null
+++ b/src/lib/dns/python/rrttl_python.h
@@ -0,0 +1,67 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_RRTTL_H
+#define __PYTHON_RRTTL_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace dns {
+class RRTTL;
+
+namespace python {
+
+extern PyObject* po_InvalidRRTTL;
+extern PyObject* po_IncompleteRRTTL;
+
+extern PyTypeObject rrttl_type;
+
+/// This is a simple shortcut to create a python RRTTL object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called within a try block
+/// followed by necessary setup for python exception.
+PyObject* createRRTTLObject(const RRTTL& source);
+
+/// \brief Checks if the given python object is a RRTTL object
+///
+/// \exception PyCPPWrapperException if obj is NULL
+///
+/// \param obj The object to check the type of
+/// \return true if the object is of type RRTTL, false otherwise
+bool PyRRTTL_Check(PyObject* obj);
+
+/// \brief Returns a reference to the RRTTL object contained within the given
+///        Python object.
+///
+/// \note The given object MUST be of type RRTTL; this can be checked with
+///       either the right call to ParseTuple("O!"), or with PyRRTTL_Check()
+///
+/// \note This is not a copy; if the RRTTL is needed when the PyObject
+/// may be destroyed, the caller must copy it itself.
+///
+/// \param rrttl_obj The rrttl object to convert
+const RRTTL& PyRRTTL_ToRRTTL(const PyObject* rrttl_obj);
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_RRTTL_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/rrtype_python.cc b/src/lib/dns/python/rrtype_python.cc
index 00e0acd..bf20b7c 100644
--- a/src/lib/dns/python/rrtype_python.cc
+++ b/src/lib/dns/python/rrtype_python.cc
@@ -12,77 +12,64 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <Python.h>
 #include <vector>
 
 #include <dns/rrtype.h>
+#include <dns/messagerenderer.h>
+#include <util/python/pycppwrapper_util.h>
+
+#include "rrtype_python.h"
+#include "messagerenderer_python.h"
+#include "pydnspp_common.h"
 
 using namespace std;
 using namespace isc::dns;
+using namespace isc::dns::python;
 using namespace isc::util;
+using namespace isc::util::python;
 
-//
-// Declaration of the custom exceptions
-// Initialization and addition of these go in the initModulePart
-// function at the end of this file
-//
-static PyObject* po_InvalidRRType;
-static PyObject* po_IncompleteRRType;
-
-//
-// Definition of the classes
-//
-
-// For each class, we need a struct, a helper functions (init, destroy,
-// and static wrappers around the methods we export), a list of methods,
-// and a type description
-
-//
-// RRType
-//
-
+namespace {
 // The s_* Class simply covers one instantiation of the object
 class s_RRType : public PyObject {
 public:
-    const RRType* rrtype;
+    const RRType* cppobj;
 };
 
-//
-// We declare the functions here, the definitions are below
-// the type definition of the object, since both can use the other
-//
-
 // General creation and destruction
-static int RRType_init(s_RRType* self, PyObject* args);
-static void RRType_destroy(s_RRType* self);
+int RRType_init(s_RRType* self, PyObject* args);
+void RRType_destroy(s_RRType* self);
 
 // These are the functions we export
-static PyObject*
+PyObject*
 RRType_toText(s_RRType* self);
 // This is a second version of toText, we need one where the argument
 // is a PyObject*, for the str() function in python.
-static PyObject* RRType_str(PyObject* self);
-static PyObject* RRType_toWire(s_RRType* self, PyObject* args);
-static PyObject* RRType_getCode(s_RRType* self);
-static PyObject* RRType_richcmp(s_RRType* self, s_RRType* other, int op);
-static PyObject* RRType_NSEC3PARAM(s_RRType *self);
-static PyObject* RRType_DNAME(s_RRType *self);
-static PyObject* RRType_PTR(s_RRType *self);
-static PyObject* RRType_MX(s_RRType *self);
-static PyObject* RRType_DNSKEY(s_RRType *self);
-static PyObject* RRType_TXT(s_RRType *self);
-static PyObject* RRType_RRSIG(s_RRType *self);
-static PyObject* RRType_NSEC(s_RRType *self);
-static PyObject* RRType_AAAA(s_RRType *self);
-static PyObject* RRType_DS(s_RRType *self);
-static PyObject* RRType_OPT(s_RRType *self);
-static PyObject* RRType_A(s_RRType *self);
-static PyObject* RRType_NS(s_RRType *self);
-static PyObject* RRType_CNAME(s_RRType *self);
-static PyObject* RRType_SOA(s_RRType *self);
-static PyObject* RRType_NSEC3(s_RRType *self);
-static PyObject* RRType_IXFR(s_RRType *self);
-static PyObject* RRType_AXFR(s_RRType *self);
-static PyObject* RRType_ANY(s_RRType *self);
+PyObject* RRType_str(PyObject* self);
+PyObject* RRType_toWire(s_RRType* self, PyObject* args);
+PyObject* RRType_getCode(s_RRType* self);
+PyObject* RRType_richcmp(s_RRType* self, s_RRType* other, int op);
+PyObject* RRType_NSEC3PARAM(s_RRType *self);
+PyObject* RRType_DNAME(s_RRType *self);
+PyObject* RRType_PTR(s_RRType *self);
+PyObject* RRType_MX(s_RRType *self);
+PyObject* RRType_DNSKEY(s_RRType *self);
+PyObject* RRType_TXT(s_RRType *self);
+PyObject* RRType_RRSIG(s_RRType *self);
+PyObject* RRType_NSEC(s_RRType *self);
+PyObject* RRType_AAAA(s_RRType *self);
+PyObject* RRType_DS(s_RRType *self);
+PyObject* RRType_OPT(s_RRType *self);
+PyObject* RRType_A(s_RRType *self);
+PyObject* RRType_NS(s_RRType *self);
+PyObject* RRType_CNAME(s_RRType *self);
+PyObject* RRType_SOA(s_RRType *self);
+PyObject* RRType_NSEC3(s_RRType *self);
+PyObject* RRType_IXFR(s_RRType *self);
+PyObject* RRType_AXFR(s_RRType *self);
+PyObject* RRType_ANY(s_RRType *self);
+
+typedef CPPPyObjectContainer<s_RRType, RRType> RRTypeContainer;
 
 // This list contains the actual set of functions we have in
 // python. Each entry has
@@ -90,7 +77,7 @@ static PyObject* RRType_ANY(s_RRType *self);
 // 2. Our static function here
 // 3. Argument type
 // 4. Documentation
-static PyMethodDef RRType_methods[] = {
+PyMethodDef RRType_methods[] = {
     { "to_text", reinterpret_cast<PyCFunction>(RRType_toText), METH_NOARGS,
       "Returns the string representation" },
     { "to_wire", reinterpret_cast<PyCFunction>(RRType_toWire), METH_VARARGS,
@@ -124,63 +111,7 @@ static PyMethodDef RRType_methods[] = {
     { NULL, NULL, 0, NULL }
 };
 
-// This defines the complete type for reflection in python and
-// parsing of PyObject* to s_RRType
-// Most of the functions are not actually implemented and NULL here.
-static PyTypeObject rrtype_type = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    "pydnspp.RRType",
-    sizeof(s_RRType),                   // tp_basicsize
-    0,                                  // tp_itemsize
-    (destructor)RRType_destroy,         // tp_dealloc
-    NULL,                               // tp_print
-    NULL,                               // tp_getattr
-    NULL,                               // tp_setattr
-    NULL,                               // tp_reserved
-    NULL,                               // tp_repr
-    NULL,                               // tp_as_number
-    NULL,                               // tp_as_sequence
-    NULL,                               // tp_as_mapping
-    NULL,                               // tp_hash 
-    NULL,                               // tp_call
-    RRType_str,                         // tp_str
-    NULL,                               // tp_getattro
-    NULL,                               // tp_setattro
-    NULL,                               // tp_as_buffer
-    Py_TPFLAGS_DEFAULT,                 // tp_flags
-    "The RRType class encapsulates DNS resource record types.\n\n"
-    "This class manages the 16-bit integer type codes in quite a straightforward "
-    "way. The only non trivial task is to handle textual representations of "
-    "RR types, such as \"A\", \"AAAA\", or \"TYPE65534\".",
-    NULL,                               // tp_traverse
-    NULL,                               // tp_clear
-    (richcmpfunc)RRType_richcmp,        // tp_richcompare
-    0,                                  // tp_weaklistoffset
-    NULL,                               // tp_iter
-    NULL,                               // tp_iternext
-    RRType_methods,                     // tp_methods
-    NULL,                               // tp_members
-    NULL,                               // tp_getset
-    NULL,                               // tp_base
-    NULL,                               // tp_dict
-    NULL,                               // tp_descr_get
-    NULL,                               // tp_descr_set
-    0,                                  // tp_dictoffset
-    (initproc)RRType_init,              // tp_init
-    NULL,                               // tp_alloc
-    PyType_GenericNew,                  // tp_new
-    NULL,                               // tp_free
-    NULL,                               // tp_is_gc
-    NULL,                               // tp_bases
-    NULL,                               // tp_mro
-    NULL,                               // tp_cache
-    NULL,                               // tp_subclasses
-    NULL,                               // tp_weaklist
-    NULL,                               // tp_del
-    0                                   // tp_version_tag
-};
-
-static int
+int
 RRType_init(s_RRType* self, PyObject* args) {
     const char* s;
     long i;
@@ -194,7 +125,7 @@ RRType_init(s_RRType* self, PyObject* args) {
     // (the way to do exceptions is to set PyErr and return -1)
     try {
         if (PyArg_ParseTuple(args, "s", &s)) {
-            self->rrtype = new RRType(s);
+            self->cppobj = new RRType(s);
             return (0);
         } else if (PyArg_ParseTuple(args, "l", &i)) {
             PyErr_Clear();
@@ -202,7 +133,7 @@ RRType_init(s_RRType* self, PyObject* args) {
                 PyErr_SetString(PyExc_ValueError, "RR Type number out of range");
                 return (-1);
             }
-            self->rrtype = new RRType(i);
+            self->cppobj = new RRType(i);
             return (0);
         } else if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) {
             Py_ssize_t size = PySequence_Size(bytes);
@@ -212,7 +143,7 @@ RRType_init(s_RRType* self, PyObject* args) {
                 return (result);
             }
             InputBuffer ib(&data[0], size);
-            self->rrtype = new RRType(ib);
+            self->cppobj = new RRType(ib);
             PyErr_Clear();
             return (0);
         }
@@ -236,36 +167,36 @@ RRType_init(s_RRType* self, PyObject* args) {
     return (-1);
 }
 
-static void
+void
 RRType_destroy(s_RRType* self) {
-    delete self->rrtype;
-    self->rrtype = NULL;
+    delete self->cppobj;
+    self->cppobj = NULL;
     Py_TYPE(self)->tp_free(self);
 }
 
-static PyObject*
+PyObject*
 RRType_toText(s_RRType* self) {
     // Py_BuildValue makes python objects from native data
-    return (Py_BuildValue("s", self->rrtype->toText().c_str()));
+    return (Py_BuildValue("s", self->cppobj->toText().c_str()));
 }
 
-static PyObject*
+PyObject*
 RRType_str(PyObject* self) {
     // Simply call the to_text method we already defined
     return (PyObject_CallMethod(self, const_cast<char*>("to_text"),
                                 const_cast<char*>("")));
 }
 
-static PyObject*
+PyObject*
 RRType_toWire(s_RRType* self, PyObject* args) {
     PyObject* bytes;
-    s_MessageRenderer* mr;
+    PyObject* mr;
 
     if (PyArg_ParseTuple(args, "O", &bytes) && PySequence_Check(bytes)) {
         PyObject* bytes_o = bytes;
 
         OutputBuffer buffer(2);
-        self->rrtype->toWire(buffer);
+        self->cppobj->toWire(buffer);
         PyObject* n = PyBytes_FromStringAndSize(static_cast<const char*>(buffer.getData()), buffer.getLength());
         PyObject* result = PySequence_InPlaceConcat(bytes_o, n);
         // We need to release the object we temporarily created here
@@ -273,7 +204,7 @@ RRType_toWire(s_RRType* self, PyObject* args) {
         Py_DECREF(n);
         return (result);
     } else if (PyArg_ParseTuple(args, "O!", &messagerenderer_type, &mr)) {
-        self->rrtype->toWire(*mr->messagerenderer);
+        self->cppobj->toWire(PyMessageRenderer_ToMessageRenderer(mr));
         // If we return NULL it is seen as an error, so use this for
         // None returns
         Py_RETURN_NONE;
@@ -284,12 +215,12 @@ RRType_toWire(s_RRType* self, PyObject* args) {
     return (NULL);
 }
 
-static PyObject*
+PyObject*
 RRType_getCode(s_RRType* self) {
-    return (Py_BuildValue("I", self->rrtype->getCode()));
+    return (Py_BuildValue("I", self->cppobj->getCode()));
 }
 
-static PyObject* 
+PyObject*
 RRType_richcmp(s_RRType* self, s_RRType* other, int op) {
     bool c;
 
@@ -301,24 +232,24 @@ RRType_richcmp(s_RRType* self, s_RRType* other, int op) {
 
     switch (op) {
     case Py_LT:
-        c = *self->rrtype < *other->rrtype;
+        c = *self->cppobj < *other->cppobj;
         break;
     case Py_LE:
-        c = *self->rrtype < *other->rrtype ||
-            *self->rrtype == *other->rrtype;
+        c = *self->cppobj < *other->cppobj ||
+            *self->cppobj == *other->cppobj;
         break;
     case Py_EQ:
-        c = *self->rrtype == *other->rrtype;
+        c = *self->cppobj == *other->cppobj;
         break;
     case Py_NE:
-        c = *self->rrtype != *other->rrtype;
+        c = *self->cppobj != *other->cppobj;
         break;
     case Py_GT:
-        c = *other->rrtype < *self->rrtype;
+        c = *other->cppobj < *self->cppobj;
         break;
     case Py_GE:
-        c = *other->rrtype < *self->rrtype ||
-            *self->rrtype == *other->rrtype;
+        c = *other->cppobj < *self->cppobj ||
+            *self->cppobj == *other->cppobj;
         break;
     default:
         PyErr_SetString(PyExc_IndexError,
@@ -334,131 +265,200 @@ RRType_richcmp(s_RRType* self, s_RRType* other, int op) {
 //
 // Common function for RRType_A/NS/etc.
 //
-static PyObject* RRType_createStatic(RRType stc) {
+PyObject* RRType_createStatic(RRType stc) {
     s_RRType* ret = PyObject_New(s_RRType, &rrtype_type);
     if (ret != NULL) {
-        ret->rrtype = new RRType(stc);
+        ret->cppobj = new RRType(stc);
     }
     return (ret);
 }
 
-static PyObject*
+PyObject*
 RRType_NSEC3PARAM(s_RRType*) {
     return (RRType_createStatic(RRType::NSEC3PARAM()));
 }
 
-static PyObject*
+PyObject*
 RRType_DNAME(s_RRType*) {
     return (RRType_createStatic(RRType::DNAME()));
 }
 
-static PyObject*
+PyObject*
 RRType_PTR(s_RRType*) {
     return (RRType_createStatic(RRType::PTR()));
 }
 
-static PyObject*
+PyObject*
 RRType_MX(s_RRType*) {
     return (RRType_createStatic(RRType::MX()));
 }
 
-static PyObject*
+PyObject*
 RRType_DNSKEY(s_RRType*) {
     return (RRType_createStatic(RRType::DNSKEY()));
 }
 
-static PyObject*
+PyObject*
 RRType_TXT(s_RRType*) {
     return (RRType_createStatic(RRType::TXT()));
 }
 
-static PyObject*
+PyObject*
 RRType_RRSIG(s_RRType*) {
     return (RRType_createStatic(RRType::RRSIG()));
 }
 
-static PyObject*
+PyObject*
 RRType_NSEC(s_RRType*) {
     return (RRType_createStatic(RRType::NSEC()));
 }
 
-static PyObject*
+PyObject*
 RRType_AAAA(s_RRType*) {
     return (RRType_createStatic(RRType::AAAA()));
 }
 
-static PyObject*
+PyObject*
 RRType_DS(s_RRType*) {
     return (RRType_createStatic(RRType::DS()));
 }
 
-static PyObject*
+PyObject*
 RRType_OPT(s_RRType*) {
     return (RRType_createStatic(RRType::OPT()));
 }
 
-static PyObject*
+PyObject*
 RRType_A(s_RRType*) {
     return (RRType_createStatic(RRType::A()));
 }
 
-static PyObject*
+PyObject*
 RRType_NS(s_RRType*) {
     return (RRType_createStatic(RRType::NS()));
 }
 
-static PyObject*
+PyObject*
 RRType_CNAME(s_RRType*) {
     return (RRType_createStatic(RRType::CNAME()));
 }
 
-static PyObject*
+PyObject*
 RRType_SOA(s_RRType*) {
     return (RRType_createStatic(RRType::SOA()));
 }
 
-static PyObject*
+PyObject*
 RRType_NSEC3(s_RRType*) {
     return (RRType_createStatic(RRType::NSEC3()));
 }
 
-static PyObject*
+PyObject*
 RRType_IXFR(s_RRType*) {
     return (RRType_createStatic(RRType::IXFR()));
 }
 
-static PyObject*
+PyObject*
 RRType_AXFR(s_RRType*) {
     return (RRType_createStatic(RRType::AXFR()));
 }
 
-static PyObject*
+PyObject*
 RRType_ANY(s_RRType*) {
     return (RRType_createStatic(RRType::ANY()));
 }
 
+} // end anonymous namespace
+
+namespace isc {
+namespace dns {
+namespace python {
 
-// end of RRType
+PyObject* po_InvalidRRType;
+PyObject* po_IncompleteRRType;
+
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_RRType
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject rrtype_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "pydnspp.RRType",
+    sizeof(s_RRType),                   // tp_basicsize
+    0,                                  // tp_itemsize
+    (destructor)RRType_destroy,         // tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash
+    NULL,                               // tp_call
+    RRType_str,                         // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    "The RRType class encapsulates DNS resource record types.\n\n"
+    "This class manages the 16-bit integer type codes in quite a straightforward "
+    "way. The only non trivial task is to handle textual representations of "
+    "RR types, such as \"A\", \"AAAA\", or \"TYPE65534\".",
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    (richcmpfunc)RRType_richcmp,        // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    RRType_methods,                     // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    (initproc)RRType_init,              // tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
 
+PyObject*
+createRRTypeObject(const RRType& source) {
+    RRTypeContainer container(PyObject_New(s_RRType, &rrtype_type));
+    container.set(new RRType(source));
+    return (container.release());
+}
 
-// Module Initialization, all statics are initialized here
 bool
-initModulePart_RRType(PyObject* mod) {
-    // Add the exceptions to the module
-    po_InvalidRRType = PyErr_NewException("pydnspp.InvalidRRType", NULL, NULL);
-    PyModule_AddObject(mod, "InvalidRRType", po_InvalidRRType);
-    po_IncompleteRRType = PyErr_NewException("pydnspp.IncompleteRRType", NULL, NULL);
-    PyModule_AddObject(mod, "IncompleteRRType", po_IncompleteRRType);
-
-    // We initialize the static description object with PyType_Ready(),
-    // then add it to the module. This is not just a check! (leaving
-    // this out results in segmentation faults)
-    if (PyType_Ready(&rrtype_type) < 0) {
-        return (false);
+PyRRType_Check(PyObject* obj) {
+    if (obj == NULL) {
+        isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck");
+    }
+    return (PyObject_TypeCheck(obj, &rrtype_type));
+}
+
+const RRType&
+PyRRType_ToRRType(const PyObject* rrtype_obj) {
+    if (rrtype_obj == NULL) {
+        isc_throw(PyCPPWrapperException,
+                  "obj argument NULL in RRType PyObject conversion");
     }
-    Py_INCREF(&rrtype_type);
-    PyModule_AddObject(mod, "RRType",
-                       reinterpret_cast<PyObject*>(&rrtype_type));
-    
-    return (true);
+    const s_RRType* rrtype = static_cast<const s_RRType*>(rrtype_obj);
+    return (*rrtype->cppobj);
 }
+
+
+} // end namespace python
+} // end namespace dns
+} // end namespace isc
diff --git a/src/lib/dns/python/rrtype_python.h b/src/lib/dns/python/rrtype_python.h
new file mode 100644
index 0000000..596598e
--- /dev/null
+++ b/src/lib/dns/python/rrtype_python.h
@@ -0,0 +1,68 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_RRTYPE_H
+#define __PYTHON_RRTYPE_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace dns {
+class RRType;
+
+namespace python {
+
+extern PyObject* po_InvalidRRType;
+extern PyObject* po_IncompleteRRType;
+
+extern PyTypeObject rrtype_type;
+
+/// This is a simple shortcut to create a python RRType object (in the
+/// form of a pointer to PyObject) with minimal exception safety.
+/// On success, it returns a valid pointer to PyObject with a reference
+/// counter of 1; if something goes wrong it throws an exception (it never
+/// returns a NULL pointer).
+/// This function is expected to be called within a try block
+/// followed by necessary setup for python exception.
+PyObject* createRRTypeObject(const RRType& source);
+
+/// \brief Checks if the given python object is a RRType object
+///
+/// \exception PyCPPWrapperException if obj is NULL
+///
+/// \param obj The object to check the type of
+/// \return true if the object is of type RRType, false otherwise
+bool PyRRType_Check(PyObject* obj);
+
+/// \brief Returns a reference to the RRType object contained within the given
+///        Python object.
+///
+/// \note The given object MUST be of type RRType; this can be checked with
+///       either the right call to ParseTuple("O!"), or with PyRRType_Check()
+///
+/// \note This is not a copy; if the RRType is needed when the PyObject
+/// may be destroyed, the caller must copy it itself.
+///
+/// \param rrtype_obj The rrtype object to convert
+const RRType& PyRRType_ToRRType(const PyObject* rrtype_obj);
+
+
+} // namespace python
+} // namespace dns
+} // namespace isc
+#endif // __PYTHON_RRTYPE_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/tests/Makefile.am b/src/lib/dns/python/tests/Makefile.am
index 61d7df6..d1273f3 100644
--- a/src/lib/dns/python/tests/Makefile.am
+++ b/src/lib/dns/python/tests/Makefile.am
@@ -24,7 +24,7 @@ EXTRA_DIST += testutil.py
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
diff --git a/src/lib/dns/python/tsig_python.cc b/src/lib/dns/python/tsig_python.cc
index db93a08..0764e33 100644
--- a/src/lib/dns/python/tsig_python.cc
+++ b/src/lib/dns/python/tsig_python.cc
@@ -37,23 +37,18 @@ using namespace isc::util::python;
 using namespace isc::dns;
 using namespace isc::dns::python;
 
-//
-// Definition of the classes
-//
-
 // For each class, we need a struct, a helper functions (init, destroy,
 // and static wrappers around the methods we export), a list of methods,
 // and a type description
 
-//
-// TSIGContext
-//
-
-// Trivial constructor.
-s_TSIGContext::s_TSIGContext() : cppobj(NULL) {
-}
-
 namespace {
+// The s_* Class simply covers one instantiation of the object
+class s_TSIGContext : public PyObject {
+public:
+    s_TSIGContext() : cppobj(NULL) {};
+    TSIGContext* cppobj;
+};
+
 // Shortcut type which would be convenient for adding class variables safely.
 typedef CPPPyObjectContainer<s_TSIGContext, TSIGContext> TSIGContextContainer;
 
@@ -101,23 +96,23 @@ int
 TSIGContext_init(s_TSIGContext* self, PyObject* args) {
     try {
         // "From key" constructor
-        const s_TSIGKey* tsigkey_obj;
+        const PyObject* tsigkey_obj;
         if (PyArg_ParseTuple(args, "O!", &tsigkey_type, &tsigkey_obj)) {
-            self->cppobj = new TSIGContext(*tsigkey_obj->cppobj);
+            self->cppobj = new TSIGContext(PyTSIGKey_ToTSIGKey(tsigkey_obj));
             return (0);
         }
 
         // "From key param + keyring" constructor
         PyErr_Clear();
-        const s_Name* keyname_obj;
-        const s_Name* algname_obj;
-        const s_TSIGKeyRing* keyring_obj;
+        const PyObject* keyname_obj;
+        const PyObject* algname_obj;
+        const PyObject* keyring_obj;
         if (PyArg_ParseTuple(args, "O!O!O!", &name_type, &keyname_obj,
                              &name_type, &algname_obj, &tsigkeyring_type,
                              &keyring_obj)) {
-            self->cppobj = new TSIGContext(*keyname_obj->cppobj,
-                                           *algname_obj->cppobj,
-                                           *keyring_obj->cppobj);
+            self->cppobj = new TSIGContext(PyName_ToName(keyname_obj),
+                                           PyName_ToName(algname_obj),
+                                           PyTSIGKeyRing_ToTSIGKeyRing(keyring_obj));
             return (0);
         }
     } catch (const exception& ex) {
@@ -153,7 +148,7 @@ PyObject*
 TSIGContext_getError(s_TSIGContext* self) {
     try {
         PyObjectContainer container(createTSIGErrorObject(
-                                        self->cppobj->getError()));
+                                    self->cppobj->getError()));
         return (Py_BuildValue("O", container.get()));
     } catch (const exception& ex) {
         const string ex_what =
@@ -205,13 +200,13 @@ PyObject*
 TSIGContext_verify(s_TSIGContext* self, PyObject* args) {
     const char* data;
     Py_ssize_t data_len;
-    s_TSIGRecord* py_record;
+    PyObject* py_record;
     PyObject* py_maybe_none;
-    TSIGRecord* record;
+    const TSIGRecord* record;
 
     if (PyArg_ParseTuple(args, "O!y#", &tsigrecord_type, &py_record,
                          &data, &data_len)) {
-        record = py_record->cppobj;
+        record = &PyTSIGRecord_ToTSIGRecord(py_record);
     } else if (PyArg_ParseTuple(args, "Oy#", &py_maybe_none, &data,
                                 &data_len)) {
         record = NULL;
@@ -264,7 +259,7 @@ PyTypeObject tsigcontext_type = {
     NULL,                               // tp_as_number
     NULL,                               // tp_as_sequence
     NULL,                               // tp_as_mapping
-    NULL,                               // tp_hash 
+    NULL,                               // tp_hash
     NULL,                               // tp_call
     NULL,                               // tp_str
     NULL,                               // tp_getattro
@@ -307,58 +302,24 @@ PyTypeObject tsigcontext_type = {
     0                                   // tp_version_tag
 };
 
-// Module Initialization, all statics are initialized here
 bool
-initModulePart_TSIGContext(PyObject* mod) {
-    // We initialize the static description object with PyType_Ready(),
-    // then add it to the module. This is not just a check! (leaving
-    // this out results in segmentation faults)
-    if (PyType_Ready(&tsigcontext_type) < 0) {
-        return (false);
+PyTSIGContext_Check(PyObject* obj) {
+    if (obj == NULL) {
+        isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck");
     }
-    void* p = &tsigcontext_type;
-    if (PyModule_AddObject(mod, "TSIGContext",
-                           static_cast<PyObject*>(p)) < 0) {
-        return (false);
-    }
-    Py_INCREF(&tsigcontext_type);
+    return (PyObject_TypeCheck(obj, &tsigcontext_type));
+}
 
-    try {
-        // Class specific exceptions
-        po_TSIGContextError = PyErr_NewException("pydnspp.TSIGContextError",
-                                                 po_IscException, NULL);
-        PyObjectContainer(po_TSIGContextError).installToModule(
-            mod, "TSIGContextError");
-
-        // Constant class variables
-        installClassVariable(tsigcontext_type, "STATE_INIT",
-                             Py_BuildValue("I", TSIGContext::INIT));
-        installClassVariable(tsigcontext_type, "STATE_SENT_REQUEST",
-                             Py_BuildValue("I", TSIGContext::SENT_REQUEST));
-        installClassVariable(tsigcontext_type, "STATE_RECEIVED_REQUEST",
-                             Py_BuildValue("I", TSIGContext::RECEIVED_REQUEST));
-        installClassVariable(tsigcontext_type, "STATE_SENT_RESPONSE",
-                             Py_BuildValue("I", TSIGContext::SENT_RESPONSE));
-        installClassVariable(tsigcontext_type, "STATE_VERIFIED_RESPONSE",
-                             Py_BuildValue("I",
-                                           TSIGContext::VERIFIED_RESPONSE));
-
-        installClassVariable(tsigcontext_type, "DEFAULT_FUDGE",
-                             Py_BuildValue("H", TSIGContext::DEFAULT_FUDGE));
-    } catch (const exception& ex) {
-        const string ex_what =
-            "Unexpected failure in TSIGContext initialization: " +
-            string(ex.what());
-        PyErr_SetString(po_IscException, ex_what.c_str());
-        return (false);
-    } catch (...) {
-        PyErr_SetString(PyExc_SystemError,
-                        "Unexpected failure in TSIGContext initialization");
-        return (false);
+TSIGContext&
+PyTSIGContext_ToTSIGContext(PyObject* tsigcontext_obj) {
+    if (tsigcontext_obj == NULL) {
+        isc_throw(PyCPPWrapperException,
+                  "obj argument NULL in TSIGContext PyObject conversion");
     }
-
-    return (true);
+    s_TSIGContext* tsigcontext = static_cast<s_TSIGContext*>(tsigcontext_obj);
+    return (*tsigcontext->cppobj);
 }
+
 } // namespace python
 } // namespace dns
 } // namespace isc
diff --git a/src/lib/dns/python/tsig_python.h b/src/lib/dns/python/tsig_python.h
index f9b4f7b..e4e9fff 100644
--- a/src/lib/dns/python/tsig_python.h
+++ b/src/lib/dns/python/tsig_python.h
@@ -23,19 +23,31 @@ class TSIGContext;
 
 namespace python {
 
-// The s_* Class simply covers one instantiation of the object
-class s_TSIGContext : public PyObject {
-public:
-    s_TSIGContext();
-    TSIGContext* cppobj;
-};
-
 extern PyTypeObject tsigcontext_type;
 
 // Class specific exceptions
 extern PyObject* po_TSIGContextError;
 
-bool initModulePart_TSIGContext(PyObject* mod);
+/// \brief Checks if the given python object is a TSIGContext object
+///
+/// \exception PyCPPWrapperException if obj is NULL
+///
+/// \param obj The object to check the type of
+/// \return true if the object is of type TSIGContext, false otherwise
+bool PyTSIGContext_Check(PyObject* obj);
+
+/// \brief Returns a reference to the TSIGContext object contained within the given
+///        Python object.
+///
+/// \note The given object MUST be of type TSIGContext; this can be checked with
+///       either the right call to ParseTuple("O!"), or with PyTSIGContext_Check()
+///
+/// \note This is not a copy; if the TSIGContext is needed when the PyObject
+/// may be destroyed, the caller must copy it itself.
+///
+/// \param tsigcontext_obj The tsigcontext object to convert
+TSIGContext& PyTSIGContext_ToTSIGContext(PyObject* tsigcontext_obj);
+
 
 } // namespace python
 } // namespace dns
diff --git a/src/lib/dns/python/tsig_rdata_python.cc b/src/lib/dns/python/tsig_rdata_python.cc
index 4e4f287..6ec0f09 100644
--- a/src/lib/dns/python/tsig_rdata_python.cc
+++ b/src/lib/dns/python/tsig_rdata_python.cc
@@ -12,6 +12,7 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#define PY_SSIZE_T_CLEAN
 #include <Python.h>
 
 #include <string>
@@ -32,23 +33,19 @@ using namespace isc::dns;
 using namespace isc::dns::rdata;
 using namespace isc::dns::python;
 
-//
-// Definition of the classes
-//
-
 // For each class, we need a struct, a helper functions (init, destroy,
 // and static wrappers around the methods we export), a list of methods,
 // and a type description
 
-//
-// TSIG RDATA
-//
+namespace {
+// The s_* Class simply covers one instantiation of the object
+class s_TSIG : public PyObject {
+public:
+    s_TSIG() : cppobj(NULL) {};
+    const rdata::any::TSIG* cppobj;
+};
 
-// Trivial constructor.
-s_TSIG::s_TSIG() : cppobj(NULL) {
-}
 
-namespace {
 // Shortcut type which would be convenient for adding class variables safely.
 typedef CPPPyObjectContainer<s_TSIG, any::TSIG> TSIGContainer;
 
@@ -235,7 +232,7 @@ TSIG_toWire(const s_TSIG* const self, PyObject* args) {
                 self, args));
 }
 
-PyObject* 
+PyObject*
 TSIG_richcmp(const s_TSIG* const self,
                    const s_TSIG* const other,
                    const int op)
@@ -302,7 +299,7 @@ PyTypeObject tsig_type = {
     NULL,                               // tp_as_number
     NULL,                               // tp_as_sequence
     NULL,                               // tp_as_mapping
-    NULL,                               // tp_hash 
+    NULL,                               // tp_hash
     NULL,                               // tp_call
     TSIG_str,                       // tp_str
     NULL,                               // tp_getattro
@@ -340,30 +337,31 @@ PyTypeObject tsig_type = {
     0                                   // tp_version_tag
 };
 
-// Module Initialization, all statics are initialized here
-bool
-initModulePart_TSIG(PyObject* mod) {
-    // We initialize the static description object with PyType_Ready(),
-    // then add it to the module. This is not just a check! (leaving
-    // this out results in segmentation faults)
-    if (PyType_Ready(&tsig_type) < 0) {
-        return (false);
-    }
-    void* p = &tsig_type;
-    if (PyModule_AddObject(mod, "TSIG", static_cast<PyObject*>(p)) < 0) {
-        return (false);
-    }
-    Py_INCREF(&tsig_type);
-
-    return (true);
-}
-
 PyObject*
 createTSIGObject(const any::TSIG& source) {
-    TSIGContainer container = PyObject_New(s_TSIG, &tsig_type);
+    TSIGContainer container(PyObject_New(s_TSIG, &tsig_type));
     container.set(new any::TSIG(source));
     return (container.release());
 }
+
+bool
+PyTSIG_Check(PyObject* obj) {
+    if (obj == NULL) {
+        isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck");
+    }
+    return (PyObject_TypeCheck(obj, &tsig_type));
+}
+
+const any::TSIG&
+PyTSIG_ToTSIG(const PyObject* tsig_obj) {
+    if (tsig_obj == NULL) {
+        isc_throw(PyCPPWrapperException,
+                  "obj argument NULL in TSIG PyObject conversion");
+    }
+    const s_TSIG* tsig = static_cast<const s_TSIG*>(tsig_obj);
+    return (*tsig->cppobj);
+}
+
 } // namespace python
 } // namespace dns
 } // namespace isc
diff --git a/src/lib/dns/python/tsig_rdata_python.h b/src/lib/dns/python/tsig_rdata_python.h
index e5e0c6c..a84d9e8 100644
--- a/src/lib/dns/python/tsig_rdata_python.h
+++ b/src/lib/dns/python/tsig_rdata_python.h
@@ -27,17 +27,8 @@ class TSIG;
 
 namespace python {
 
-// The s_* Class simply covers one instantiation of the object
-class s_TSIG : public PyObject {
-public:
-    s_TSIG();
-    const rdata::any::TSIG* cppobj;
-};
-
 extern PyTypeObject tsig_type;
 
-bool initModulePart_TSIG(PyObject* mod);
-
 /// This is A simple shortcut to create a python TSIG object (in the
 /// form of a pointer to PyObject) with minimal exception safety.
 /// On success, it returns a valid pointer to PyObject with a reference
@@ -47,6 +38,26 @@ bool initModulePart_TSIG(PyObject* mod);
 /// followed by necessary setup for python exception.
 PyObject* createTSIGObject(const rdata::any::TSIG& source);
 
+/// \brief Checks if the given python object is a TSIG object
+///
+/// \exception PyCPPWrapperException if obj is NULL
+///
+/// \param obj The object to check the type of
+/// \return true if the object is of type TSIG, false otherwise
+bool PyTSIG_Check(PyObject* obj);
+
+/// \brief Returns a reference to the TSIG object contained within the given
+///        Python object.
+///
+/// \note The given object MUST be of type TSIG; this can be checked with
+///       either the right call to ParseTuple("O!"), or with PyTSIG_Check()
+///
+/// \note This is not a copy; if the TSIG is needed when the PyObject
+/// may be destroyed, the caller must copy it itself.
+///
+/// \param tsig_obj The tsig object to convert
+const rdata::any::TSIG& PyTSIG_ToTSIG(const PyObject* tsig_obj);
+
 } // namespace python
 } // namespace dns
 } // namespace isc
diff --git a/src/lib/dns/python/tsigerror_python.cc b/src/lib/dns/python/tsigerror_python.cc
index 0ad4716..7a0217e 100644
--- a/src/lib/dns/python/tsigerror_python.cc
+++ b/src/lib/dns/python/tsigerror_python.cc
@@ -30,26 +30,21 @@ using namespace isc::util::python;
 using namespace isc::dns;
 using namespace isc::dns::python;
 
-//
-// Definition of the classes
-//
-
 // For each class, we need a struct, a helper functions (init, destroy,
 // and static wrappers around the methods we export), a list of methods,
 // and a type description
 
-//
-// TSIGError
-//
-
-// Trivial constructor.
-s_TSIGError::s_TSIGError() : cppobj(NULL) {
-}
-
 // Import pydoc text
 #include "tsigerror_python_inc.cc"
 
 namespace {
+// The s_* Class simply covers one instantiation of the object
+class s_TSIGError : public PyObject {
+public:
+    s_TSIGError() : cppobj(NULL) {};
+    const TSIGError* cppobj;
+};
+
 // Shortcut type which would be convenient for adding class variables safely.
 typedef CPPPyObjectContainer<s_TSIGError, TSIGError> TSIGErrorContainer;
 
@@ -107,9 +102,9 @@ TSIGError_init(s_TSIGError* self, PyObject* args) {
 
         // Constructor from Rcode
         PyErr_Clear();
-        s_Rcode* py_rcode;
+        PyObject* py_rcode;
         if (PyArg_ParseTuple(args, "O!", &rcode_type, &py_rcode)) {
-            self->cppobj = new TSIGError(*py_rcode->cppobj);
+            self->cppobj = new TSIGError(PyRcode_ToRcode(py_rcode));
             return (0);
         }
     } catch (const isc::OutOfRange& ex) {
@@ -172,13 +167,8 @@ TSIGError_str(PyObject* self) {
 
 PyObject*
 TSIGError_toRcode(const s_TSIGError* const self) {
-    typedef CPPPyObjectContainer<s_Rcode, Rcode> RcodePyObjectContainer;
-
     try {
-        RcodePyObjectContainer rcode_container(PyObject_New(s_Rcode,
-                                                            &rcode_type));
-        rcode_container.set(new Rcode(self->cppobj->toRcode()));
-        return (rcode_container.release());
+        return (createRcodeObject(self->cppobj->toRcode()));
     } catch (const exception& ex) {
         const string ex_what =
             "Failed to convert TSIGError to Rcode: " + string(ex.what());
@@ -190,7 +180,7 @@ TSIGError_toRcode(const s_TSIGError* const self) {
     return (NULL);
 }
 
-PyObject* 
+PyObject*
 TSIGError_richcmp(const s_TSIGError* const self,
                    const s_TSIGError* const other,
                    const int op)
@@ -252,7 +242,7 @@ PyTypeObject tsigerror_type = {
     NULL,                               // tp_as_number
     NULL,                               // tp_as_sequence
     NULL,                               // tp_as_mapping
-    NULL,                               // tp_hash 
+    NULL,                               // tp_hash
     NULL,                               // tp_call
     // THIS MAY HAVE TO BE CHANGED TO NULL:
     TSIGError_str,                       // tp_str
@@ -290,78 +280,9 @@ PyTypeObject tsigerror_type = {
     0                                   // tp_version_tag
 };
 
-namespace {
-// Trivial shortcut to create and install TSIGError constants.
-inline void
-installTSIGErrorConstant(const char* name, const TSIGError& val) {
-    TSIGErrorContainer container(PyObject_New(s_TSIGError, &tsigerror_type));
-    container.installAsClassVariable(tsigerror_type, name, new TSIGError(val));
-}
-}
-
-// Module Initialization, all statics are initialized here
-bool
-initModulePart_TSIGError(PyObject* mod) {
-    // We initialize the static description object with PyType_Ready(),
-    // then add it to the module. This is not just a check! (leaving
-    // this out results in segmentation faults)
-    if (PyType_Ready(&tsigerror_type) < 0) {
-        return (false);
-    }
-    void* p = &tsigerror_type;
-    if (PyModule_AddObject(mod, "TSIGError", static_cast<PyObject*>(p)) < 0) {
-        return (false);
-    }
-    Py_INCREF(&tsigerror_type);
-
-    try {
-        // Constant class variables
-        // Error codes (bare values)
-        installClassVariable(tsigerror_type, "BAD_SIG_CODE",
-                             Py_BuildValue("H", TSIGError::BAD_SIG_CODE));
-        installClassVariable(tsigerror_type, "BAD_KEY_CODE",
-                             Py_BuildValue("H", TSIGError::BAD_KEY_CODE));
-        installClassVariable(tsigerror_type, "BAD_TIME_CODE",
-                             Py_BuildValue("H", TSIGError::BAD_TIME_CODE));
-
-        // Error codes (constant objects)
-        installTSIGErrorConstant("NOERROR", TSIGError::NOERROR());
-        installTSIGErrorConstant("FORMERR", TSIGError::FORMERR());
-        installTSIGErrorConstant("SERVFAIL", TSIGError::SERVFAIL());
-        installTSIGErrorConstant("NXDOMAIN", TSIGError::NXDOMAIN());
-        installTSIGErrorConstant("NOTIMP", TSIGError::NOTIMP());
-        installTSIGErrorConstant("REFUSED", TSIGError::REFUSED());
-        installTSIGErrorConstant("YXDOMAIN", TSIGError::YXDOMAIN());
-        installTSIGErrorConstant("YXRRSET", TSIGError::YXRRSET());
-        installTSIGErrorConstant("NXRRSET", TSIGError::NXRRSET());
-        installTSIGErrorConstant("NOTAUTH", TSIGError::NOTAUTH());
-        installTSIGErrorConstant("NOTZONE", TSIGError::NOTZONE());
-        installTSIGErrorConstant("RESERVED11", TSIGError::RESERVED11());
-        installTSIGErrorConstant("RESERVED12", TSIGError::RESERVED12());
-        installTSIGErrorConstant("RESERVED13", TSIGError::RESERVED13());
-        installTSIGErrorConstant("RESERVED14", TSIGError::RESERVED14());
-        installTSIGErrorConstant("RESERVED15", TSIGError::RESERVED15());
-        installTSIGErrorConstant("BAD_SIG", TSIGError::BAD_SIG());
-        installTSIGErrorConstant("BAD_KEY", TSIGError::BAD_KEY());
-        installTSIGErrorConstant("BAD_TIME", TSIGError::BAD_TIME());
-    } catch (const exception& ex) {
-        const string ex_what =
-            "Unexpected failure in TSIGError initialization: " +
-            string(ex.what());
-        PyErr_SetString(po_IscException, ex_what.c_str());
-        return (false);
-    } catch (...) {
-        PyErr_SetString(PyExc_SystemError,
-                        "Unexpected failure in TSIGError initialization");
-        return (false);
-    }
-
-    return (true);
-}
-
 PyObject*
 createTSIGErrorObject(const TSIGError& source) {
-    TSIGErrorContainer container = PyObject_New(s_TSIGError, &tsigerror_type);
+    TSIGErrorContainer container(PyObject_New(s_TSIGError, &tsigerror_type));
     container.set(new TSIGError(source));
     return (container.release());
 }
diff --git a/src/lib/dns/python/tsigerror_python.h b/src/lib/dns/python/tsigerror_python.h
index 735a480..0b5b630 100644
--- a/src/lib/dns/python/tsigerror_python.h
+++ b/src/lib/dns/python/tsigerror_python.h
@@ -23,17 +23,8 @@ class TSIGError;
 
 namespace python {
 
-// The s_* Class simply covers one instantiation of the object
-class s_TSIGError : public PyObject {
-public:
-    s_TSIGError();
-    const TSIGError* cppobj;
-};
-
 extern PyTypeObject tsigerror_type;
 
-bool initModulePart_TSIGError(PyObject* mod);
-
 /// This is A simple shortcut to create a python TSIGError object (in the
 /// form of a pointer to PyObject) with minimal exception safety.
 /// On success, it returns a valid pointer to PyObject with a reference
@@ -42,6 +33,7 @@ bool initModulePart_TSIGError(PyObject* mod);
 /// This function is expected to be called with in a try block
 /// followed by necessary setup for python exception.
 PyObject* createTSIGErrorObject(const TSIGError& source);
+
 } // namespace python
 } // namespace dns
 } // namespace isc
diff --git a/src/lib/dns/python/tsigkey_python.cc b/src/lib/dns/python/tsigkey_python.cc
index f0906cb..cf79c1a 100644
--- a/src/lib/dns/python/tsigkey_python.cc
+++ b/src/lib/dns/python/tsigkey_python.cc
@@ -31,10 +31,6 @@ using namespace isc::util::python;
 using namespace isc::dns;
 using namespace isc::dns::python;
 
-//
-// Definition of the classes
-//
-
 // For each class, we need a struct, a helper functions (init, destroy,
 // and static wrappers around the methods we export), a list of methods,
 // and a type description
@@ -43,11 +39,14 @@ using namespace isc::dns::python;
 // TSIGKey
 //
 
+namespace {
 // The s_* Class simply covers one instantiation of the object
+class s_TSIGKey : public PyObject {
+public:
+    s_TSIGKey() : cppobj(NULL) {};
+    TSIGKey* cppobj;
+};
 
-s_TSIGKey::s_TSIGKey() : cppobj(NULL) {}
-
-namespace {
 //
 // We declare the functions here, the definitions are below
 // the type definition of the object, since both can use the other
@@ -96,8 +95,8 @@ TSIGKey_init(s_TSIGKey* self, PyObject* args) {
         }
 
         PyErr_Clear();
-        const s_Name* key_name;
-        const s_Name* algorithm_name;
+        const PyObject* key_name;
+        const PyObject* algorithm_name;
         PyObject* bytes_obj;
         const char* secret;
         Py_ssize_t secret_len;
@@ -107,8 +106,8 @@ TSIGKey_init(s_TSIGKey* self, PyObject* args) {
             if (secret_len == 0) {
                 secret = NULL;
             }
-            self->cppobj = new TSIGKey(*key_name->cppobj,
-                                       *algorithm_name->cppobj,
+            self->cppobj = new TSIGKey(PyName_ToName(key_name),
+                                       PyName_ToName(algorithm_name),
                                        secret, secret_len);
             return (0);
         }
@@ -196,7 +195,7 @@ PyTypeObject tsigkey_type = {
     NULL,                               // tp_as_number
     NULL,                               // tp_as_sequence
     NULL,                               // tp_as_mapping
-    NULL,                               // tp_hash 
+    NULL,                               // tp_hash
     NULL,                               // tp_call
     NULL,                               // tp_str
     NULL,                               // tp_getattro
@@ -233,49 +232,20 @@ PyTypeObject tsigkey_type = {
     0                                   // tp_version_tag
 };
 
-// Module Initialization, all statics are initialized here
 bool
-initModulePart_TSIGKey(PyObject* mod) {
-    // We initialize the static description object with PyType_Ready(),
-    // then add it to the module. This is not just a check! (leaving
-    // this out results in segmentation faults)
-    if (PyType_Ready(&tsigkey_type) < 0) {
-        return (false);
-    }
-    void* p = &tsigkey_type;
-    if (PyModule_AddObject(mod, "TSIGKey", static_cast<PyObject*>(p)) != 0) {
-        return (false);
-    }
-    Py_INCREF(&tsigkey_type);
-
-    try {
-        // Constant class variables
-        installClassVariable(tsigkey_type, "HMACMD5_NAME",
-                             createNameObject(TSIGKey::HMACMD5_NAME()));
-        installClassVariable(tsigkey_type, "HMACSHA1_NAME",
-                             createNameObject(TSIGKey::HMACSHA1_NAME()));
-        installClassVariable(tsigkey_type, "HMACSHA256_NAME",
-                             createNameObject(TSIGKey::HMACSHA256_NAME()));
-        installClassVariable(tsigkey_type, "HMACSHA224_NAME",
-                             createNameObject(TSIGKey::HMACSHA224_NAME()));
-        installClassVariable(tsigkey_type, "HMACSHA384_NAME",
-                             createNameObject(TSIGKey::HMACSHA384_NAME()));
-        installClassVariable(tsigkey_type, "HMACSHA512_NAME",
-                             createNameObject(TSIGKey::HMACSHA512_NAME()));
-    } catch (const exception& ex) {
-        const string ex_what =
-            "Unexpected failure in TSIGKey initialization: " +
-            string(ex.what());
-        PyErr_SetString(po_IscException, ex_what.c_str());
-        return (false);
-    } catch (...) {
-        PyErr_SetString(PyExc_SystemError,
-                        "Unexpected failure in TSIGKey initialization");
-        return (false);
+PyTSIGKey_Check(PyObject* obj) {
+    if (obj == NULL) {
+        isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck");
     }
+    return (PyObject_TypeCheck(obj, &tsigkey_type));
+}
 
-    return (true);
+const TSIGKey&
+PyTSIGKey_ToTSIGKey(const PyObject* tsigkey_obj) {
+    const s_TSIGKey* tsigkey = static_cast<const s_TSIGKey*>(tsigkey_obj);
+    return (*tsigkey->cppobj);
 }
+
 } // namespace python
 } // namespace dns
 } // namespace isc
@@ -287,13 +257,14 @@ initModulePart_TSIGKey(PyObject* mod) {
 // TSIGKeyRing
 //
 
+namespace {
 // The s_* Class simply covers one instantiation of the object
+class s_TSIGKeyRing : public PyObject {
+public:
+    s_TSIGKeyRing() : cppobj(NULL) {};
+    TSIGKeyRing* cppobj;
+};
 
-// The s_* Class simply covers one instantiation of the object
-
-s_TSIGKeyRing::s_TSIGKeyRing() : cppobj(NULL) {}
-
-namespace {
 //
 // We declare the functions here, the definitions are below
 // the type definition of the object, since both can use the other
@@ -329,7 +300,7 @@ TSIGKeyRing_init(s_TSIGKeyRing* self, PyObject* args) {
                         "Invalid arguments to TSIGKeyRing constructor");
         return (-1);
     }
-    
+
     self->cppobj = new(nothrow) TSIGKeyRing();
     if (self->cppobj == NULL) {
         PyErr_SetString(po_IscException, "Allocating TSIGKeyRing failed");
@@ -354,7 +325,7 @@ TSIGKeyRing_size(const s_TSIGKeyRing* const self) {
 PyObject*
 TSIGKeyRing_add(const s_TSIGKeyRing* const self, PyObject* args) {
     s_TSIGKey* tsigkey;
-    
+
     if (PyArg_ParseTuple(args, "O!", &tsigkey_type, &tsigkey)) {
         try {
             const TSIGKeyRing::Result result =
@@ -374,11 +345,11 @@ TSIGKeyRing_add(const s_TSIGKeyRing* const self, PyObject* args) {
 
 PyObject*
 TSIGKeyRing_remove(const s_TSIGKeyRing* self, PyObject* args) {
-    s_Name* key_name;
+    PyObject* key_name;
 
     if (PyArg_ParseTuple(args, "O!", &name_type, &key_name)) {
         const TSIGKeyRing::Result result =
-            self->cppobj->remove(*key_name->cppobj);
+            self->cppobj->remove(PyName_ToName(key_name));
         return (Py_BuildValue("I", result));
     }
 
@@ -390,13 +361,14 @@ TSIGKeyRing_remove(const s_TSIGKeyRing* self, PyObject* args) {
 
 PyObject*
 TSIGKeyRing_find(const s_TSIGKeyRing* self, PyObject* args) {
-    s_Name* key_name;
-    s_Name* algorithm_name;
+    PyObject* key_name;
+    PyObject* algorithm_name;
 
     if (PyArg_ParseTuple(args, "O!O!", &name_type, &key_name,
                          &name_type, &algorithm_name)) {
         const TSIGKeyRing::FindResult result =
-            self->cppobj->find(*key_name->cppobj, *algorithm_name->cppobj);
+            self->cppobj->find(PyName_ToName(key_name),
+                               PyName_ToName(algorithm_name));
         if (result.key != NULL) {
             s_TSIGKey* key = PyObject_New(s_TSIGKey, &tsigkey_type);
             if (key == NULL) {
@@ -436,7 +408,7 @@ PyTypeObject tsigkeyring_type = {
     NULL,                               // tp_as_number
     NULL,                               // tp_as_sequence
     NULL,                               // tp_as_mapping
-    NULL,                               // tp_hash 
+    NULL,                               // tp_hash
     NULL,                               // tp_call
     NULL,                               // tp_str
     NULL,                               // tp_getattro
@@ -473,27 +445,24 @@ PyTypeObject tsigkeyring_type = {
 };
 
 bool
-initModulePart_TSIGKeyRing(PyObject* mod) {
-    if (PyType_Ready(&tsigkeyring_type) < 0) {
-        return (false);
+PyTSIGKeyRing_Check(PyObject* obj) {
+    if (obj == NULL) {
+        isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck");
     }
-    Py_INCREF(&tsigkeyring_type);
-    void* p = &tsigkeyring_type;
-    if (PyModule_AddObject(mod, "TSIGKeyRing",
-                           static_cast<PyObject*>(p)) != 0) {
-        Py_DECREF(&tsigkeyring_type);
-        return (false);
-    }
-
-    addClassVariable(tsigkeyring_type, "SUCCESS",
-                     Py_BuildValue("I", TSIGKeyRing::SUCCESS));
-    addClassVariable(tsigkeyring_type, "EXIST",
-                     Py_BuildValue("I", TSIGKeyRing::EXIST));
-    addClassVariable(tsigkeyring_type, "NOTFOUND",
-                     Py_BuildValue("I", TSIGKeyRing::NOTFOUND));
+    return (PyObject_TypeCheck(obj, &tsigkeyring_type));
+}
 
-    return (true);
+const TSIGKeyRing&
+PyTSIGKeyRing_ToTSIGKeyRing(const PyObject* tsigkeyring_obj) {
+    if (tsigkeyring_obj == NULL) {
+        isc_throw(PyCPPWrapperException,
+                  "obj argument NULL in TSIGKeyRing PyObject conversion");
+    }
+    const s_TSIGKeyRing* tsigkeyring =
+        static_cast<const s_TSIGKeyRing*>(tsigkeyring_obj);
+    return (*tsigkeyring->cppobj);
 }
+
 } // namespace python
 } // namespace dns
 } // namespace isc
diff --git a/src/lib/dns/python/tsigkey_python.h b/src/lib/dns/python/tsigkey_python.h
index 51b3ae7..6c3d2e3 100644
--- a/src/lib/dns/python/tsigkey_python.h
+++ b/src/lib/dns/python/tsigkey_python.h
@@ -24,24 +24,46 @@ class TSIGKeyRing;
 
 namespace python {
 
-// The s_* Class simply covers one instantiation of the object
-class s_TSIGKey : public PyObject {
-public:
-    s_TSIGKey();
-    TSIGKey* cppobj;
-};
-
-class s_TSIGKeyRing : public PyObject {
-public:
-    s_TSIGKeyRing();
-    TSIGKeyRing* cppobj;
-};
-
 extern PyTypeObject tsigkey_type;
 extern PyTypeObject tsigkeyring_type;
 
-bool initModulePart_TSIGKey(PyObject* mod);
-bool initModulePart_TSIGKeyRing(PyObject* mod);
+/// \brief Checks if the given python object is a TSIGKey object
+///
+/// \exception PyCPPWrapperException if obj is NULL
+///
+/// \param obj The object to check the type of
+/// \return true if the object is of type TSIGKey, false otherwise
+bool PyTSIGKey_Check(PyObject* obj);
+
+/// \brief Returns a reference to the TSIGKey object contained within the given
+///        Python object.
+///
+/// \note The given object MUST be of type TSIGKey; this can be checked with
+///       either the right call to ParseTuple("O!"), or with PyTSIGKey_Check()
+///
+/// \note This is not a copy; if the TSIGKey is needed when the PyObject
+/// may be destroyed, the caller must copy it itself.
+///
+/// \param tsigkey_obj The tsigkey object to convert
+const TSIGKey& PyTSIGKey_ToTSIGKey(const PyObject* tsigkey_obj);
+
+/// \brief Checks if the given python object is a TSIGKeyRing object
+///
+/// \param obj The object to check the type of
+/// \return true if the object is of type TSIGKeyRing, false otherwise
+bool PyTSIGKeyRing_Check(PyObject* obj);
+
+/// \brief Returns a reference to the TSIGKeyRing object contained within the given
+///        Python object.
+///
+/// \note The given object MUST be of type TSIGKeyRing; this can be checked with
+///       either the right call to ParseTuple("O!"), or with PyTSIGKeyRing_Check()
+///
+/// \note This is not a copy; if the TSIGKeyRing is needed when the PyObject
+/// may be destroyed, the caller must copy it itself.
+///
+/// \param tsigkeyring_obj The tsigkeyring object to convert
+const TSIGKeyRing& PyTSIGKeyRing_ToTSIGKeyRing(const PyObject* tsigkeyring_obj);
 
 } // namespace python
 } // namespace dns
diff --git a/src/lib/dns/python/tsigrecord_python.cc b/src/lib/dns/python/tsigrecord_python.cc
index 8a78b5e..c754dd2 100644
--- a/src/lib/dns/python/tsigrecord_python.cc
+++ b/src/lib/dns/python/tsigrecord_python.cc
@@ -32,10 +32,6 @@ using namespace isc::util::python;
 using namespace isc::dns;
 using namespace isc::dns::python;
 
-//
-// Definition of the classes
-//
-
 // For each class, we need a struct, a helper functions (init, destroy,
 // and static wrappers around the methods we export), a list of methods,
 // and a type description
@@ -44,11 +40,14 @@ using namespace isc::dns::python;
 // TSIGRecord
 //
 
-// Trivial constructor.
-s_TSIGRecord::s_TSIGRecord() : cppobj(NULL) {
-}
-
 namespace {
+// The s_* Class simply covers one instantiation of the object
+class s_TSIGRecord : public PyObject {
+public:
+    s_TSIGRecord() : cppobj(NULL) {};
+    TSIGRecord* cppobj;
+};
+
 // Shortcut type which would be convenient for adding class variables safely.
 typedef CPPPyObjectContainer<s_TSIGRecord, TSIGRecord> TSIGRecordContainer;
 
@@ -102,11 +101,12 @@ PyMethodDef TSIGRecord_methods[] = {
 int
 TSIGRecord_init(s_TSIGRecord* self, PyObject* args) {
     try {
-        const s_Name* py_name;
-        const s_TSIG* py_tsig;
+        const PyObject* py_name;
+        const PyObject* py_tsig;
         if (PyArg_ParseTuple(args, "O!O!", &name_type, &py_name,
                              &tsig_type, &py_tsig)) {
-            self->cppobj = new TSIGRecord(*py_name->cppobj, *py_tsig->cppobj);
+            self->cppobj = new TSIGRecord(PyName_ToName(py_name),
+                                          PyTSIG_ToTSIG(py_tsig));
             return (0);
         }
     } catch (const exception& ex) {
@@ -226,7 +226,7 @@ PyTypeObject tsigrecord_type = {
     NULL,                               // tp_as_number
     NULL,                               // tp_as_sequence
     NULL,                               // tp_as_mapping
-    NULL,                               // tp_hash 
+    NULL,                               // tp_hash
     NULL,                               // tp_call
     TSIGRecord_str,                       // tp_str
     NULL,                               // tp_getattro
@@ -262,50 +262,32 @@ PyTypeObject tsigrecord_type = {
     0                                   // tp_version_tag
 };
 
-// Module Initialization, all statics are initialized here
+PyObject*
+createTSIGRecordObject(const TSIGRecord& source) {
+    TSIGRecordContainer container(PyObject_New(s_TSIGRecord, &tsigrecord_type));
+    container.set(new TSIGRecord(source));
+    return (container.release());
+}
+
 bool
-initModulePart_TSIGRecord(PyObject* mod) {
-    // We initialize the static description object with PyType_Ready(),
-    // then add it to the module. This is not just a check! (leaving
-    // this out results in segmentation faults)
-    if (PyType_Ready(&tsigrecord_type) < 0) {
-        return (false);
-    }
-    void* p = &tsigrecord_type;
-    if (PyModule_AddObject(mod, "TSIGRecord", static_cast<PyObject*>(p)) < 0) {
-        return (false);
+PyTSIGRecord_Check(PyObject* obj) {
+    if (obj == NULL) {
+        isc_throw(PyCPPWrapperException, "obj argument NULL in typecheck");
     }
-    Py_INCREF(&tsigrecord_type);
+    return (PyObject_TypeCheck(obj, &tsigrecord_type));
+}
 
-    // The following template is the typical procedure for installing class
-    // variables.  If the class doesn't have a class variable, remove the
-    // entire try-catch clauses.
-    try {
-        // Constant class variables
-        installClassVariable(tsigrecord_type, "TSIG_TTL",
-                             Py_BuildValue("I", 0));
-    } catch (const exception& ex) {
-        const string ex_what =
-            "Unexpected failure in TSIGRecord initialization: " +
-            string(ex.what());
-        PyErr_SetString(po_IscException, ex_what.c_str());
-        return (false);
-    } catch (...) {
-        PyErr_SetString(PyExc_SystemError,
-                        "Unexpected failure in TSIGRecord initialization");
-        return (false);
+const TSIGRecord&
+PyTSIGRecord_ToTSIGRecord(PyObject* tsigrecord_obj) {
+    if (tsigrecord_obj == NULL) {
+        isc_throw(PyCPPWrapperException,
+                  "obj argument NULL in TSIGRecord PyObject conversion");
     }
-
-    return (true);
+    s_TSIGRecord* tsigrecord = static_cast<s_TSIGRecord*>(tsigrecord_obj);
+    return (*tsigrecord->cppobj);
 }
 
-PyObject*
-createTSIGRecordObject(const TSIGRecord& source) {
-    TSIGRecordContainer container = PyObject_New(s_TSIGRecord,
-                                                 &tsigrecord_type);
-    container.set(new TSIGRecord(source));
-    return (container.release());
-}
+
 } // namespace python
 } // namespace dns
 } // namespace isc
diff --git a/src/lib/dns/python/tsigrecord_python.h b/src/lib/dns/python/tsigrecord_python.h
index e0a3526..d6252e1 100644
--- a/src/lib/dns/python/tsigrecord_python.h
+++ b/src/lib/dns/python/tsigrecord_python.h
@@ -23,17 +23,9 @@ class TSIGRecord;
 
 namespace python {
 
-// The s_* Class simply covers one instantiation of the object
-class s_TSIGRecord : public PyObject {
-public:
-    s_TSIGRecord();
-    TSIGRecord* cppobj;
-};
 
 extern PyTypeObject tsigrecord_type;
 
-bool initModulePart_TSIGRecord(PyObject* mod);
-
 /// This is A simple shortcut to create a python TSIGRecord object (in the
 /// form of a pointer to PyObject) with minimal exception safety.
 /// On success, it returns a valid pointer to PyObject with a reference
@@ -43,6 +35,26 @@ bool initModulePart_TSIGRecord(PyObject* mod);
 /// followed by necessary setup for python exception.
 PyObject* createTSIGRecordObject(const TSIGRecord& source);
 
+/// \brief Checks if the given python object is a TSIGRecord object
+///
+/// \exception PyCPPWrapperException if obj is NULL
+///
+/// \param obj The object to check the type of
+/// \return true if the object is of type TSIGRecord, false otherwise
+bool PyTSIGRecord_Check(PyObject* obj);
+
+/// \brief Returns a reference to the TSIGRecord object contained within the given
+///        Python object.
+///
+/// \note The given object MUST be of type TSIGRecord; this can be checked with
+///       either the right call to ParseTuple("O!"), or with PyTSIGRecord_Check()
+///
+/// \note This is not a copy; if the TSIGRecord is needed when the PyObject
+/// may be destroyed, the caller must copy it itself.
+///
+/// \param rrtype_obj The rrtype object to convert
+const TSIGRecord& PyTSIGRecord_ToTSIGRecord(PyObject* tsigrecord_obj);
+
 } // namespace python
 } // namespace dns
 } // namespace isc
diff --git a/src/lib/dns/rdata/generic/hinfo_13.cc b/src/lib/dns/rdata/generic/hinfo_13.cc
new file mode 100644
index 0000000..45f4209
--- /dev/null
+++ b/src/lib/dns/rdata/generic/hinfo_13.cc
@@ -0,0 +1,129 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <string>
+
+#include <boost/lexical_cast.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/name.h>
+#include <dns/messagerenderer.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/character_string.h>
+#include <util/strutil.h>
+
+using namespace std;
+using namespace boost;
+using namespace isc::util;
+using namespace isc::dns;
+using namespace isc::dns::characterstr;
+
+// BEGIN_ISC_NAMESPACE
+// BEGIN_RDATA_NAMESPACE
+
+
+HINFO::HINFO(const string& hinfo_str) {
+    string::const_iterator input_iterator = hinfo_str.begin();
+    cpu_ = getNextCharacterString(hinfo_str, input_iterator);
+
+    skipLeftSpaces(hinfo_str, input_iterator);
+
+    os_ = getNextCharacterString(hinfo_str, input_iterator);
+}
+
+HINFO::HINFO(InputBuffer& buffer, size_t rdata_len) {
+    cpu_ = getNextCharacterString(buffer, rdata_len);
+    os_ = getNextCharacterString(buffer, rdata_len);
+}
+
+HINFO::HINFO(const HINFO& source):
+    Rdata(), cpu_(source.cpu_), os_(source.os_)
+{
+}
+
+std::string
+HINFO::toText() const {
+    string result;
+    result += "\"";
+    result += cpu_;
+    result += "\" \"";
+    result += os_;
+    result += "\"";
+    return (result);
+}
+
+void
+HINFO::toWire(OutputBuffer& buffer) const {
+    toWireHelper(buffer);
+}
+
+void
+HINFO::toWire(AbstractMessageRenderer& renderer) const {
+    toWireHelper(renderer);
+}
+
+int
+HINFO::compare(const Rdata& other) const {
+    const HINFO& other_hinfo = dynamic_cast<const HINFO&>(other);
+
+    if (cpu_ < other_hinfo.cpu_) {
+        return (-1);
+    } else if (cpu_ > other_hinfo.cpu_) {
+        return (1);
+    }
+
+    if (os_ < other_hinfo.os_) {
+        return (-1);
+    } else if (os_ > other_hinfo.os_) {
+        return (1);
+    }
+
+    return (0);
+}
+
+const std::string&
+HINFO::getCPU() const {
+    return (cpu_);
+}
+
+const std::string&
+HINFO::getOS() const {
+    return (os_);
+}
+
+void
+HINFO::skipLeftSpaces(const std::string& input_str,
+                      std::string::const_iterator& input_iterator)
+{
+    if (input_iterator >= input_str.end()) {
+        isc_throw(InvalidRdataText,
+                  "Invalid HINFO text format, field is missing.");
+    }
+
+    if (!isspace(*input_iterator)) {
+        isc_throw(InvalidRdataText,
+            "Invalid HINFO text format, fields are not separated by space.");
+    }
+    // Skip white spaces
+    while (input_iterator < input_str.end() && isspace(*input_iterator)) {
+        ++input_iterator;
+    }
+}
+
+// END_RDATA_NAMESPACE
+// END_ISC_NAMESPACE
diff --git a/src/lib/dns/rdata/generic/hinfo_13.h b/src/lib/dns/rdata/generic/hinfo_13.h
new file mode 100644
index 0000000..8513419
--- /dev/null
+++ b/src/lib/dns/rdata/generic/hinfo_13.h
@@ -0,0 +1,77 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// BEGIN_HEADER_GUARD
+#include <stdint.h>
+
+#include <string>
+
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <util/buffer.h>
+
+// BEGIN_ISC_NAMESPACE
+
+// BEGIN_COMMON_DECLARATIONS
+// END_COMMON_DECLARATIONS
+
+// BEGIN_RDATA_NAMESPACE
+
+/// \brief \c HINFO class represents the HINFO rdata defined in
+/// RFC1034, RFC1035
+///
+/// This class implements the basic interfaces inherited from the
+/// \c rdata::Rdata class, and provides accessors specific to the
+/// HINFO rdata.
+class HINFO : public Rdata {
+public:
+    // BEGIN_COMMON_MEMBERS
+    // END_COMMON_MEMBERS
+
+    // HINFO specific methods
+    const std::string& getCPU() const;
+    const std::string& getOS() const;
+
+private:
+    /// Skip the left whitespaces of the input string
+    ///
+    /// \param input_str The input string
+    /// \param input_iterator From which the skipping started
+    void skipLeftSpaces(const std::string& input_str,
+                        std::string::const_iterator& input_iterator);
+
+    /// Helper template function for toWire()
+    ///
+    /// \param outputer Where to write data in
+    template <typename T>
+    void toWireHelper(T& outputer) const {
+        outputer.writeUint8(cpu_.size());
+        outputer.writeData(cpu_.c_str(), cpu_.size());
+
+        outputer.writeUint8(os_.size());
+        outputer.writeData(os_.c_str(), os_.size());
+    }
+
+    std::string cpu_;
+    std::string os_;
+};
+
+
+// END_RDATA_NAMESPACE
+// END_ISC_NAMESPACE
+// END_HEADER_GUARD
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/rdata/generic/naptr_35.cc b/src/lib/dns/rdata/generic/naptr_35.cc
index 5268331..129bf6c 100644
--- a/src/lib/dns/rdata/generic/naptr_35.cc
+++ b/src/lib/dns/rdata/generic/naptr_35.cc
@@ -20,6 +20,7 @@
 
 #include <exceptions/exceptions.h>
 
+#include <dns/character_string.h>
 #include <dns/name.h>
 #include <dns/messagerenderer.h>
 #include <dns/rdata.h>
@@ -28,6 +29,8 @@
 using namespace std;
 using namespace boost;
 using namespace isc::util;
+using namespace isc::dns;
+using namespace isc::dns::characterstr;
 
 // BEGIN_ISC_NAMESPACE
 // BEGIN_RDATA_NAMESPACE
@@ -56,79 +59,6 @@ skipLeftSpaces(const std::string& input_str,
     }
 }
 
-/// Get a <character-string> from a string
-///
-/// \param input_str The input string
-/// \param input_iterator The iterator from which to start extracting,
-///        the iterator will be updated to new position after the function
-///        is returned
-/// \return A std::string that contains the extracted <character-string>
-std::string
-getNextCharacterString(const std::string& input_str,
-                       std::string::const_iterator& input_iterator)
-{
-    string result;
-
-    // If the input string only contains white-spaces, it is an invalid
-    // <character-string>
-    if (input_iterator >= input_str.end()) {
-        isc_throw(InvalidRdataText, "Invalid NAPTR text format, \
-                  <character-string> field is missing.");
-    }
-
-    // Whether the <character-string> is separated with double quotes (")
-    bool quotes_separated = (*input_iterator == '"');
-
-    if (quotes_separated) {
-        ++input_iterator;
-    }
-
-    while(input_iterator < input_str.end()){
-        if (quotes_separated) {
-            // If the <character-string> is seperated with quotes symbol and
-            // another quotes symbol is encountered, it is the end of the
-            // <character-string>
-            if (*input_iterator == '"') {
-                ++input_iterator;
-                break;
-            }
-        } else if (*input_iterator == ' ') {
-            // If the <character-string> is not seperated with quotes symbol,
-            // it is seperated with <space> char
-            break;
-        }
-
-        result.push_back(*input_iterator);
-
-        ++input_iterator;
-    }
-
-    if (result.size() > MAX_CHARSTRING_LEN) {
-        isc_throw(CharStringTooLong, "NAPTR <character-string> is too long");
-    }
-
-    return (result);
-}
-
-/// Get a <character-string> from a input buffer
-///
-/// \param buffer The input buffer
-/// \param len The input buffer total length
-/// \return A std::string that contains the extracted <character-string>
-std::string
-getNextCharacterString(InputBuffer& buffer, size_t len) {
-    uint8_t str_len = buffer.readUint8();
-
-    size_t pos = buffer.getPosition();
-    if (len - pos < str_len) {
-        isc_throw(InvalidRdataLength, "Invalid NAPTR string length");
-    }
-
-    uint8_t buf[MAX_CHARSTRING_LEN];
-    buffer.readData(buf, str_len);
-    return (string(buf, buf + str_len));
-}
-
 } // Anonymous namespace
 
 NAPTR::NAPTR(InputBuffer& buffer, size_t len):
@@ -194,36 +124,12 @@ NAPTR::NAPTR(const NAPTR& naptr):
 
 void
 NAPTR::toWire(OutputBuffer& buffer) const {
-    buffer.writeUint16(order_);
-    buffer.writeUint16(preference_);
-
-    buffer.writeUint8(flags_.size());
-    buffer.writeData(flags_.c_str(), flags_.size());
-
-    buffer.writeUint8(services_.size());
-    buffer.writeData(services_.c_str(), services_.size());
-
-    buffer.writeUint8(regexp_.size());
-    buffer.writeData(regexp_.c_str(), regexp_.size());
-
-    replacement_.toWire(buffer);
+    toWireHelper(buffer);
 }
 
 void
 NAPTR::toWire(AbstractMessageRenderer& renderer) const {
-    renderer.writeUint16(order_);
-    renderer.writeUint16(preference_);
-
-    renderer.writeUint8(flags_.size());
-    renderer.writeData(flags_.c_str(), flags_.size());
-
-    renderer.writeUint8(services_.size());
-    renderer.writeData(services_.c_str(), services_.size());
-
-    renderer.writeUint8(regexp_.size());
-    renderer.writeData(regexp_.c_str(), regexp_.size());
-
-    replacement_.toWire(renderer);
+    toWireHelper(renderer);
 }
 
 string
diff --git a/src/lib/dns/rdata/generic/naptr_35.h b/src/lib/dns/rdata/generic/naptr_35.h
index b3015ae..ca16b3c 100644
--- a/src/lib/dns/rdata/generic/naptr_35.h
+++ b/src/lib/dns/rdata/generic/naptr_35.h
@@ -46,6 +46,26 @@ public:
     const std::string& getRegexp() const;
     const Name& getReplacement() const;
 private:
+    /// Helper template function for toWire()
+    ///
+    /// \param outputer Where to write data in
+    template <typename T>
+    void toWireHelper(T& outputer) const {
+        outputer.writeUint16(order_);
+        outputer.writeUint16(preference_);
+
+        outputer.writeUint8(flags_.size());
+        outputer.writeData(flags_.c_str(), flags_.size());
+
+        outputer.writeUint8(services_.size());
+        outputer.writeData(services_.c_str(), services_.size());
+
+        outputer.writeUint8(regexp_.size());
+        outputer.writeData(regexp_.c_str(), regexp_.size());
+
+        replacement_.toWire(outputer);
+    }
+
     uint16_t order_;
     uint16_t preference_;
     std::string flags_;
diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am
index 48bce85..5f90cea 100644
--- a/src/lib/dns/tests/Makefile.am
+++ b/src/lib/dns/tests/Makefile.am
@@ -46,6 +46,7 @@ run_unittests_SOURCES += rdata_srv_unittest.cc
 run_unittests_SOURCES += rdata_minfo_unittest.cc
 run_unittests_SOURCES += rdata_tsig_unittest.cc
 run_unittests_SOURCES += rdata_naptr_unittest.cc
+run_unittests_SOURCES += rdata_hinfo_unittest.cc
 run_unittests_SOURCES += rrset_unittest.cc rrsetlist_unittest.cc
 run_unittests_SOURCES += question_unittest.cc
 run_unittests_SOURCES += rrparamregistry_unittest.cc
@@ -55,6 +56,7 @@ run_unittests_SOURCES += tsig_unittest.cc
 run_unittests_SOURCES += tsigerror_unittest.cc
 run_unittests_SOURCES += tsigkey_unittest.cc
 run_unittests_SOURCES += tsigrecord_unittest.cc
+run_unittests_SOURCES += character_string_unittest.cc
 run_unittests_SOURCES += run_unittests.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 # We shouldn't need to include BOTAN_LDFLAGS here, but there
diff --git a/src/lib/dns/tests/character_string_unittest.cc b/src/lib/dns/tests/character_string_unittest.cc
new file mode 100644
index 0000000..5fed9eb
--- /dev/null
+++ b/src/lib/dns/tests/character_string_unittest.cc
@@ -0,0 +1,92 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+
+#include <gtest/gtest.h>
+
+#include <dns/rdata.h>
+#include <dns/tests/unittest_util.h>
+#include <dns/character_string.h>
+
+using isc::UnitTestUtil;
+
+using namespace std;
+using namespace isc;
+using namespace isc::dns;
+using namespace isc::dns::characterstr;
+using namespace isc::dns::rdata;
+
+namespace {
+
+class CharacterString {
+public:
+    CharacterString(const string& str){
+        string::const_iterator it = str.begin();
+        characterStr_ = getNextCharacterString(str, it);
+    }
+    const string& str() const { return characterStr_; }
+private:
+    string characterStr_;
+};
+
+TEST(CharacterStringTest, testNormalCase) {
+    CharacterString cstr1("foo");
+    EXPECT_EQ(string("foo"), cstr1.str());
+
+    // Test <character-string> that separated by space
+    CharacterString cstr2("foo bar");
+    EXPECT_EQ(string("foo"), cstr2.str());
+
+    // Test <character-string> that separated by quotes
+    CharacterString cstr3("\"foo bar\"");
+    EXPECT_EQ(string("foo bar"), cstr3.str());
+
+    // Test <character-string> that not separate by quotes but ended with quotes
+    CharacterString cstr4("foo\"");
+    EXPECT_EQ(string("foo\""), cstr4.str());
+}
+
+TEST(CharacterStringTest, testBadCase) {
+    // The <character-string> that started with quotes should also be ended
+    // with quotes
+    EXPECT_THROW(CharacterString cstr("\"foo"), InvalidRdataText);
+
+    // The string length cannot exceed 255 characters
+    string str;
+    for (int i = 0; i < 257; ++i) {
+        str += 'A';
+    }
+    EXPECT_THROW(CharacterString cstr(str), CharStringTooLong);
+}
+
+TEST(CharacterStringTest, testEscapeCharacter) {
+    CharacterString cstr1("foo\\bar");
+    EXPECT_EQ(string("foobar"), cstr1.str());
+
+    CharacterString cstr2("foo\\\\bar");
+    EXPECT_EQ(string("foo\\bar"), cstr2.str());
+
+    CharacterString cstr3("fo\\111bar");
+    EXPECT_EQ(string("foobar"), cstr3.str());
+
+    CharacterString cstr4("fo\\1112bar");
+    EXPECT_EQ(string("foo2bar"), cstr4.str());
+
+    // There must be at least 3 digits followed by '\'
+    EXPECT_THROW(CharacterString cstr("foo\\98ar"), InvalidRdataText);
+    EXPECT_THROW(CharacterString cstr("foo\\9ar"), InvalidRdataText);
+    EXPECT_THROW(CharacterString cstr("foo\\98"), InvalidRdataText);
+}
+
+} // namespace
diff --git a/src/lib/dns/tests/rdata_hinfo_unittest.cc b/src/lib/dns/tests/rdata_hinfo_unittest.cc
new file mode 100644
index 0000000..c52b2a0
--- /dev/null
+++ b/src/lib/dns/tests/rdata_hinfo_unittest.cc
@@ -0,0 +1,115 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/buffer.h>
+#include <dns/exceptions.h>
+#include <dns/messagerenderer.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+
+#include <gtest/gtest.h>
+
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
+
+using isc::UnitTestUtil;
+using namespace std;
+using namespace isc::dns;
+using namespace isc::util;
+using namespace isc::dns::rdata;
+using namespace isc::dns::rdata::generic;
+
+namespace {
+class Rdata_HINFO_Test : public RdataTest {
+};
+
+static uint8_t hinfo_rdata[] = {0x07,0x50,0x65,0x6e,0x74,0x69,0x75,0x6d,0x05,
+    0x4c,0x69,0x6e,0x75,0x78};
+static const char *hinfo_str = "\"Pentium\" \"Linux\"";
+static const char *hinfo_str1 = "\"Pen\\\"tium\" \"Linux\"";
+
+static const char *hinfo_str_small1 = "\"Lentium\" \"Linux\"";
+static const char *hinfo_str_small2 = "\"Pentium\" \"Kinux\"";
+static const char *hinfo_str_large1 = "\"Qentium\" \"Linux\"";
+static const char *hinfo_str_large2 = "\"Pentium\" \"UNIX\"";
+
+TEST_F(Rdata_HINFO_Test, createFromText) {
+    HINFO hinfo(hinfo_str);
+    EXPECT_EQ(string("Pentium"), hinfo.getCPU());
+    EXPECT_EQ(string("Linux"), hinfo.getOS());
+
+    // Test the text with double quotes in the middle of string
+    HINFO hinfo1(hinfo_str1);
+    EXPECT_EQ(string("Pen\"tium"), hinfo1.getCPU());
+}
+
+TEST_F(Rdata_HINFO_Test, badText) {
+    // Fields must be seperated by spaces
+    EXPECT_THROW(const HINFO hinfo("\"Pentium\"\"Linux\""), InvalidRdataText);
+    // Field cannot be missing
+    EXPECT_THROW(const HINFO hinfo("Pentium"), InvalidRdataText);
+    // The <character-string> cannot exceed 255 characters
+    string hinfo_str;
+    for (int i = 0; i < 257; ++i) {
+        hinfo_str += 'A';
+    }
+    hinfo_str += " Linux";
+    EXPECT_THROW(const HINFO hinfo(hinfo_str), CharStringTooLong);
+}
+
+TEST_F(Rdata_HINFO_Test, createFromWire) {
+    InputBuffer input_buffer(hinfo_rdata, sizeof(hinfo_rdata));
+    HINFO hinfo(input_buffer, sizeof(hinfo_rdata));
+    EXPECT_EQ(string("Pentium"), hinfo.getCPU());
+    EXPECT_EQ(string("Linux"), hinfo.getOS());
+}
+
+TEST_F(Rdata_HINFO_Test, toText) {
+    HINFO hinfo(hinfo_str);
+    EXPECT_EQ(hinfo_str, hinfo.toText());
+}
+
+TEST_F(Rdata_HINFO_Test, toWire) {
+    HINFO hinfo(hinfo_str);
+    hinfo.toWire(obuffer);
+
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
+                        obuffer.getLength(), hinfo_rdata, sizeof(hinfo_rdata));
+}
+
+TEST_F(Rdata_HINFO_Test, toWireRenderer) {
+    HINFO hinfo(hinfo_str);
+
+    hinfo.toWire(renderer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
+                        obuffer.getLength(), hinfo_rdata, sizeof(hinfo_rdata));
+}
+
+TEST_F(Rdata_HINFO_Test, compare) {
+    HINFO hinfo(hinfo_str);
+    HINFO hinfo_small1(hinfo_str_small1);
+    HINFO hinfo_small2(hinfo_str_small2);
+    HINFO hinfo_large1(hinfo_str_large1);
+    HINFO hinfo_large2(hinfo_str_large2);
+
+    EXPECT_EQ(0, hinfo.compare(HINFO(hinfo_str)));
+    EXPECT_EQ(1, hinfo.compare(HINFO(hinfo_str_small1)));
+    EXPECT_EQ(1, hinfo.compare(HINFO(hinfo_str_small2)));
+    EXPECT_EQ(-1, hinfo.compare(HINFO(hinfo_str_large1)));
+    EXPECT_EQ(-1, hinfo.compare(HINFO(hinfo_str_large2)));
+}
+
+}
diff --git a/src/lib/python/isc/Makefile.am b/src/lib/python/isc/Makefile.am
index d94100b..f90f7b6 100644
--- a/src/lib/python/isc/Makefile.am
+++ b/src/lib/python/isc/Makefile.am
@@ -1,4 +1,5 @@
-SUBDIRS = datasrc cc config log net notify util testutils acl bind10
+SUBDIRS = datasrc cc config dns log net notify util testutils acl bind10
+SUBDIRS += log_messages
 
 python_PYTHON = __init__.py
 
diff --git a/src/lib/python/isc/__init__.py b/src/lib/python/isc/__init__.py
index 8fcbf42..029f110 100644
--- a/src/lib/python/isc/__init__.py
+++ b/src/lib/python/isc/__init__.py
@@ -1,4 +1,7 @@
-import isc.datasrc
+# On some systems, it appears the dynamic linker gets
+# confused if the order is not right here
+# There is probably a solution for this, but for now:
+# order is important here!
 import isc.cc
 import isc.config
-#import isc.dns
+import isc.datasrc
diff --git a/src/lib/python/isc/acl/tests/Makefile.am b/src/lib/python/isc/acl/tests/Makefile.am
index 87781d7..e0a1895 100644
--- a/src/lib/python/isc/acl/tests/Makefile.am
+++ b/src/lib/python/isc/acl/tests/Makefile.am
@@ -7,7 +7,7 @@ EXTRA_DIST = $(PYTESTS)
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/acl/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/acl/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
@@ -19,7 +19,7 @@ if ENABLE_PYTHON_COVERAGE
 endif
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
-	env PYTHONPATH=$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/isc/python/acl/.libs:$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/isc/python/acl/.libs \
 	$(LIBRARY_PATH_PLACEHOLDER) \
 	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
 	done
diff --git a/src/lib/python/isc/bind10/sockcreator.py b/src/lib/python/isc/bind10/sockcreator.py
index 9fcc74e..8e5b019 100644
--- a/src/lib/python/isc/bind10/sockcreator.py
+++ b/src/lib/python/isc/bind10/sockcreator.py
@@ -17,7 +17,7 @@ import socket
 import struct
 import os
 import subprocess
-from bind10_messages import *
+from isc.log_messages.bind10_messages import *
 from libutil_io_python import recv_fd
 
 logger = isc.log.Logger("boss")
diff --git a/src/lib/python/isc/bind10/tests/Makefile.am b/src/lib/python/isc/bind10/tests/Makefile.am
index f498b86..df8ab30 100644
--- a/src/lib/python/isc/bind10/tests/Makefile.am
+++ b/src/lib/python/isc/bind10/tests/Makefile.am
@@ -9,7 +9,7 @@ EXTRA_DIST = $(PYTESTS)
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
@@ -22,7 +22,7 @@ endif
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	$(LIBRARY_PATH_PLACEHOLDER) \
-	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_srcdir)/src/bin:$(abs_top_builddir)/src/bin/bind10:$(abs_top_builddir)/src/lib/util/io/.libs \
 	BIND10_MSGQ_SOCKET_FILE=$(abs_top_builddir)/msgq_socket \
 		$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
 	done
diff --git a/src/lib/python/isc/cc/tests/Makefile.am b/src/lib/python/isc/cc/tests/Makefile.am
index 4e49501..4c2acc0 100644
--- a/src/lib/python/isc/cc/tests/Makefile.am
+++ b/src/lib/python/isc/cc/tests/Makefile.am
@@ -10,7 +10,7 @@ EXTRA_DIST += test_session.py
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
@@ -23,7 +23,7 @@ endif
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	$(LIBRARY_PATH_PLACEHOLDER) \
-	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python \
+	PYTHONPATH=$(COMMON_PYTHON_PATH) \
 	BIND10_TEST_SOCKET_FILE=$(builddir)/test_socket.sock \
 	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
 	done
diff --git a/src/lib/python/isc/config/Makefile.am b/src/lib/python/isc/config/Makefile.am
index 312ad33..ef696fb 100644
--- a/src/lib/python/isc/config/Makefile.am
+++ b/src/lib/python/isc/config/Makefile.am
@@ -1,27 +1,31 @@
 SUBDIRS = . tests
 
 python_PYTHON = __init__.py ccsession.py cfgmgr.py config_data.py module_spec.py
-pyexec_DATA = cfgmgr_messages.py $(top_builddir)/src/lib/python/config_messages.py
-
 pythondir = $(pyexecdir)/isc/config
 
-# Define rule to build logging source files from message file
-cfgmgr_messages.py: cfgmgr_messages.mes
-	$(top_builddir)/src/lib/log/compiler/message \
-	-p $(top_srcdir)/src/lib/python/isc/config/cfgmgr_messages.mes
-
-$(top_builddir)/src/lib/python/config_messages.py: config_messages.mes
-	$(top_builddir)/src/lib/log/compiler/message \
-		-p -d $(top_builddir)/src/lib/python \
-		$(top_srcdir)/src/lib/python/isc/config/config_messages.mes
+BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/work/cfgmgr_messages.py
+BUILT_SOURCES += $(PYTHON_LOGMSGPKG_DIR)/work/config_messages.py
+nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/cfgmgr_messages.py
+nodist_pylogmessage_PYTHON += $(PYTHON_LOGMSGPKG_DIR)/work/config_messages.py
+pylogmessagedir = $(pyexecdir)/isc/log_messages/
 
-CLEANFILES =  cfgmgr_messages.py cfgmgr_messages.pyc
-CLEANFILES += $(top_builddir)/src/lib/python/config_messages.py
-CLEANFILES += $(top_builddir)/src/lib/python/config_messages.pyc
+CLEANFILES = $(PYTHON_LOGMSGPKG_DIR)/work/cfgmgr_messages.py
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/cfgmgr_messages.pyc
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/config_messages.py
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/config_messages.pyc
 
 CLEANDIRS = __pycache__
 
 EXTRA_DIST = cfgmgr_messages.mes config_messages.mes
 
+# Define rule to build logging source files from message file
+$(PYTHON_LOGMSGPKG_DIR)/work/cfgmgr_messages.py : cfgmgr_messages.mes
+	$(top_builddir)/src/lib/log/compiler/message \
+	-d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/cfgmgr_messages.mes
+
+$(PYTHON_LOGMSGPKG_DIR)/work/config_messages.py : config_messages.mes
+	$(top_builddir)/src/lib/log/compiler/message \
+	-d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/config_messages.mes
+
 clean-local:
 	rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py
index ba7724c..d07df1e 100644
--- a/src/lib/python/isc/config/ccsession.py
+++ b/src/lib/python/isc/config/ccsession.py
@@ -43,7 +43,7 @@ from isc.util.file import path_search
 import bind10_config
 from isc.log import log_config_update
 import json
-from config_messages import *
+from isc.log_messages.config_messages import *
 
 logger = isc.log.Logger("config")
 
diff --git a/src/lib/python/isc/config/cfgmgr.py b/src/lib/python/isc/config/cfgmgr.py
index 1db9fd3..9996a19 100644
--- a/src/lib/python/isc/config/cfgmgr.py
+++ b/src/lib/python/isc/config/cfgmgr.py
@@ -32,7 +32,7 @@ from isc.config import ccsession, config_data, module_spec
 from isc.util.file import path_search
 import bind10_config
 import isc.log
-from cfgmgr_messages import *
+from isc.log_messages.cfgmgr_messages import *
 
 logger = isc.log.Logger("cfgmgr")
 
diff --git a/src/lib/python/isc/config/tests/Makefile.am b/src/lib/python/isc/config/tests/Makefile.am
index 47ccc41..6670ee7 100644
--- a/src/lib/python/isc/config/tests/Makefile.am
+++ b/src/lib/python/isc/config/tests/Makefile.am
@@ -8,7 +8,7 @@ EXTRA_DIST += unittest_fakesession.py
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
@@ -21,7 +21,7 @@ endif
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	$(LIBRARY_PATH_PLACEHOLDER) \
-	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/python/isc/config \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/python/isc/config \
 	B10_TEST_PLUGIN_DIR=$(abs_top_srcdir)/src/bin/cfgmgr/plugins \
 	CONFIG_TESTDATA_PATH=$(abs_top_srcdir)/src/lib/config/tests/testdata \
 	CONFIG_WR_TESTDATA_PATH=$(abs_top_builddir)/src/lib/config/tests/testdata \
diff --git a/src/lib/python/isc/datasrc/Makefile.am b/src/lib/python/isc/datasrc/Makefile.am
index 46fb661..d8f6b08 100644
--- a/src/lib/python/isc/datasrc/Makefile.am
+++ b/src/lib/python/isc/datasrc/Makefile.am
@@ -1,10 +1,39 @@
 SUBDIRS = . tests
 
+# old data, should be removed in the near future once conversion is done
 python_PYTHON = __init__.py master.py sqlite3_ds.py
 
-pythondir = $(pyexecdir)/isc/datasrc
+#pythondir = $(pyexecdir)/isc/pydatasrc
+
+# new data
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(SQLITE_CFLAGS)
+
+pythondir = $(pyexecdir)/isc
+pyexec_LTLIBRARIES = datasrc.la
+datasrc_la_SOURCES = datasrc.cc datasrc.h
+datasrc_la_SOURCES += client_python.cc client_python.h
+datasrc_la_SOURCES += iterator_python.cc iterator_python.h
+datasrc_la_SOURCES += finder_python.cc finder_python.h
+datasrc_la_SOURCES += updater_python.cc updater_python.h
+
+datasrc_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
+datasrc_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
+datasrc_la_LDFLAGS = $(PYTHON_LDFLAGS)
+datasrc_la_LDFLAGS += -module
+datasrc_la_LIBADD = $(top_builddir)/src/lib/datasrc/libdatasrc.la
+datasrc_la_LIBADD += $(top_builddir)/src/lib/dns/python/libpydnspp.la
+datasrc_la_LIBADD += $(PYTHON_LIB)
+#datasrc_la_LIBADD += $(SQLITE_LIBS)
+
+EXTRA_DIST = client_inc.cc
+EXTRA_DIST += finder_inc.cc
+EXTRA_DIST += iterator_inc.cc
+EXTRA_DIST += updater_inc.cc
 
 CLEANDIRS = __pycache__
 
 clean-local:
 	rm -rf $(CLEANDIRS)
+
diff --git a/src/lib/python/isc/datasrc/__init__.py b/src/lib/python/isc/datasrc/__init__.py
index 0e1e481..53711db 100644
--- a/src/lib/python/isc/datasrc/__init__.py
+++ b/src/lib/python/isc/datasrc/__init__.py
@@ -1,2 +1,12 @@
-from isc.datasrc.master import *
+import sys
+import os
+
+for base in sys.path[:]:
+    datasrc_libdir = os.path.join(base, 'isc/datasrc/.libs')
+    if os.path.exists(datasrc_libdir):
+        sys.path.insert(0, datasrc_libdir)
+
+from datasrc import *
 from isc.datasrc.sqlite3_ds import *
+from isc.datasrc.master import *
+
diff --git a/src/lib/python/isc/datasrc/client_inc.cc b/src/lib/python/isc/datasrc/client_inc.cc
new file mode 100644
index 0000000..1eba488
--- /dev/null
+++ b/src/lib/python/isc/datasrc/client_inc.cc
@@ -0,0 +1,157 @@
+namespace {
+
+const char* const DataSourceClient_doc = "\
+The base class of data source clients.\n\
+\n\
+This is the python wrapper for the abstract base class that defines\n\
+the common interface for various types of data source clients. A data\n\
+source client is a top level access point to a data source, allowing \n\
+various operations on the data source such as lookups, traversing or \n\
+updates. The client class itself has limited focus and delegates \n\
+the responsibility for these specific operations to other (c++) classes;\n\
+in general methods of this class act as factories of these other classes.\n\
+\n\
+- InMemoryClient: A client of a conceptual data source that stores all\n\
+  necessary data in memory for faster lookups\n\
+- DatabaseClient: A client that uses a real database backend (such as\n\
+  an SQL database). It would internally hold a connection to the\n\
+  underlying database system.\n\
+\n\
+It is intentional that while the term these derived classes don't\n\
+contain \"DataSource\" unlike their base class. It's also noteworthy\n\
+that the naming of the base class is somewhat redundant because the\n\
+namespace datasrc would indicate that it's related to a data source.\n\
+The redundant naming comes from the observation that namespaces are\n\
+often omitted with using directives, in which case \"Client\" would be\n\
+too generic. On the other hand, concrete derived classes are generally\n\
+not expected to be referenced directly from other modules and\n\
+applications, so we'll give them more concise names such as\n\
+InMemoryClient. A single DataSourceClient object is expected to handle\n\
+only a single RR class even if the underlying data source contains\n\
+records for multiple RR classes. Likewise, (when we support views) a\n\
+DataSourceClient object is expected to handle only a single view.\n\
+\n\
+If the application uses multiple threads, each thread will need to\n\
+create and use a separate DataSourceClient. This is because some\n\
+database backend doesn't allow multiple threads to share the same\n\
+connection to the database.\n\
+\n\
+For a client using an in memory backend, this may result in having a\n\
+multiple copies of the same data in memory, increasing the memory\n\
+footprint substantially. Depending on how to support multiple CPU\n\
+cores for concurrent lookups on the same single data source (which is\n\
+not fully fixed yet, and for which multiple threads may be used), this\n\
+design may have to be revisited. This class (and therefore its derived\n\
+classes) are not copyable. This is because the derived classes would\n\
+generally contain attributes that are not easy to copy (such as a\n\
+large size of in memory data or a network connection to a database\n\
+server). In order to avoid a surprising disruption with a naive copy\n\
+it's prohibited explicitly. For the expected usage of the client\n\
+classes the restriction should be acceptable.\n\
+\n\
+Todo: This class is still not complete. It will need more factory\n\
+methods, e.g. for (re)loading a zone.\n\
+";
+
+const char* const DataSourceClient_findZone_doc = "\
+find_zone(name) -> (code, ZoneFinder)\n\
+\n\
+Returns a ZoneFinder for a zone that best matches the given name.\n\
+\n\
+code: The result code of the operation (integer).\n\
+- DataSourceClient.SUCCESS: A zone that gives an exact match is found\n\
+- DataSourceClient.PARTIALMATCH: A zone whose origin is a super domain of name\n\
+  is found (but there is no exact match)\n\
+- DataSourceClient.NOTFOUND: For all other cases.\n\
+ZoneFinder: ZoneFinder object for the found zone if one is found;\n\
+otherwise None.\n\
+\n\
+Any internal error will be raised as an isc.datasrc.Error exception\n\
+\n\
+Parameters:\n\
+  name       A domain name for which the search is performed.\n\
+\n\
+Return Value(s): A tuple containing a result value and a ZoneFinder object or\n\
+None\n\
+";
+
+const char* const DataSourceClient_getIterator_doc = "\
+get_iterator(name) -> ZoneIterator\n\
+\n\
+Returns an iterator to the given zone.\n\
+\n\
+This allows for traversing the whole zone. The returned object can\n\
+provide the RRsets one by one.\n\
+\n\
+This throws isc.datasrc.Error when the zone does not exist in the\n\
+datasource, or when an internal error occurs.\n\
+\n\
+The default implementation throws isc.datasrc.NotImplemented. This allows for\n\
+easy and fast deployment of minimal custom data sources, where the\n\
+user/implementator doesn't have to care about anything else but the\n\
+actual queries. Also, in some cases, it isn't possible to traverse the\n\
+zone from logic point of view (eg. dynamically generated zone data).\n\
+\n\
+It is not fixed if a concrete implementation of this method can throw\n\
+anything else.\n\
+\n\
+Parameters:\n\
+  isc.dns.Name The name of zone apex to be traversed. It doesn't do\n\
+               nearest match as find_zone.\n\
+\n\
+Return Value(s): Pointer to the iterator.\n\
+";
+
+const char* const DataSourceClient_getUpdater_doc = "\
+get_updater(name, replace) -> ZoneUpdater\n\
+\n\
+Return an updater to make updates to a specific zone.\n\
+\n\
+The RR class of the zone is the one that the client is expected to\n\
+handle (see the detailed description of this class).\n\
+\n\
+If the specified zone is not found via the client, a NULL pointer will\n\
+be returned; in other words a completely new zone cannot be created\n\
+using an updater. It must be created beforehand (even if it's an empty\n\
+placeholder) in a way specific to the underlying data source.\n\
+\n\
+Conceptually, the updater will trigger a separate transaction for\n\
+subsequent updates to the zone within the context of the updater (the\n\
+actual implementation of the \"transaction\" may vary for the specific\n\
+underlying data source). Until commit() is performed on the updater,\n\
+the intermediate updates won't affect the results of other methods\n\
+(and the result of the object's methods created by other factory\n\
+methods). Likewise, if the updater is destructed without performing\n\
+commit(), the intermediate updates will be effectively canceled and\n\
+will never affect other methods.\n\
+\n\
+If the underlying data source allows concurrent updates, this method\n\
+can be called multiple times while the previously returned updater(s)\n\
+are still active. In this case each updater triggers a different\n\
+\"transaction\". Normally it would be for different zones for such a\n\
+case as handling multiple incoming AXFR streams concurrently, but this\n\
+interface does not even prohibit an attempt of getting more than one\n\
+updater for the same zone, as long as the underlying data source\n\
+allows such an operation (and any conflict resolution is left to the\n\
+specific implementation).\n\
+\n\
+If replace is true, any existing RRs of the zone will be deleted on\n\
+successful completion of updates (after commit() on the updater); if\n\
+it's false, the existing RRs will be intact unless explicitly deleted\n\
+by delete_rrset() on the updater.\n\
+\n\
+A data source can be \"read only\" or can prohibit partial updates. In\n\
+such cases this method will result in an isc.datasrc.NotImplemented exception\n\
+unconditionally or when replace is false).\n\
+\n\
+Exceptions:\n\
+  isc.datasrc. NotImplemented The underlying data source does not support\n\
+               updates.\n\
+  isc.datasrc.Error Internal error in the underlying data source.\n\
+\n\
+Parameters:\n\
+  name       The zone name to be updated\n\
+  replace    Whether to delete existing RRs before making updates\n\
+\n\
+";
+} // unnamed namespace
diff --git a/src/lib/python/isc/datasrc/client_python.cc b/src/lib/python/isc/datasrc/client_python.cc
new file mode 100644
index 0000000..984eabf
--- /dev/null
+++ b/src/lib/python/isc/datasrc/client_python.cc
@@ -0,0 +1,264 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// Enable this if you use s# variants with PyArg_ParseTuple(), see
+// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
+//#define PY_SSIZE_T_CLEAN
+
+// Python.h needs to be placed at the head of the program file, see:
+// http://docs.python.org/py3k/extending/extending.html#a-simple-example
+#include <Python.h>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <datasrc/client.h>
+#include <datasrc/database.h>
+#include <datasrc/data_source.h>
+#include <datasrc/sqlite3_accessor.h>
+#include <datasrc/iterator.h>
+
+#include <dns/python/name_python.h>
+#include <dns/python/rrset_python.h>
+#include <dns/python/pydnspp_common.h>
+
+#include "datasrc.h"
+#include "client_python.h"
+#include "finder_python.h"
+#include "iterator_python.h"
+#include "updater_python.h"
+#include "client_inc.cc"
+
+using namespace std;
+using namespace isc::util::python;
+using namespace isc::dns::python;
+using namespace isc::datasrc;
+using namespace isc::datasrc::python;
+
+namespace {
+// The s_* Class simply covers one instantiation of the object
+class s_DataSourceClient : public PyObject {
+public:
+    s_DataSourceClient() : cppobj(NULL) {};
+    DataSourceClient* cppobj;
+};
+
+// Shortcut type which would be convenient for adding class variables safely.
+typedef CPPPyObjectContainer<s_DataSourceClient, DataSourceClient>
+    DataSourceClientContainer;
+
+PyObject*
+DataSourceClient_findZone(PyObject* po_self, PyObject* args) {
+    s_DataSourceClient* const self = static_cast<s_DataSourceClient*>(po_self);
+    PyObject *name;
+    if (PyArg_ParseTuple(args, "O!", &name_type, &name)) {
+        try {
+            DataSourceClient::FindResult find_result(
+                self->cppobj->findZone(PyName_ToName(name)));
+
+            result::Result r = find_result.code;
+            ZoneFinderPtr zfp = find_result.zone_finder;
+            // Use N instead of O so refcount isn't increased twice
+            return (Py_BuildValue("IN", r, createZoneFinderObject(zfp)));
+        } catch (const std::exception& exc) {
+            PyErr_SetString(getDataSourceException("Error"), exc.what());
+            return (NULL);
+        } catch (...) {
+            PyErr_SetString(getDataSourceException("Error"),
+                            "Unexpected exception");
+            return (NULL);
+        }
+    } else {
+        return (NULL);
+    }
+}
+
+PyObject*
+DataSourceClient_getIterator(PyObject* po_self, PyObject* args) {
+    s_DataSourceClient* const self = static_cast<s_DataSourceClient*>(po_self);
+    PyObject *name_obj;
+    if (PyArg_ParseTuple(args, "O!", &name_type, &name_obj)) {
+        try {
+            return (createZoneIteratorObject(
+                        self->cppobj->getIterator(PyName_ToName(name_obj))));
+        } catch (const isc::NotImplemented& ne) {
+            PyErr_SetString(getDataSourceException("NotImplemented"),
+                            ne.what());
+            return (NULL);
+        } catch (const DataSourceError& dse) {
+            PyErr_SetString(getDataSourceException("Error"), dse.what());
+            return (NULL);
+        } catch (const std::exception& exc) {
+            PyErr_SetString(getDataSourceException("Error"), exc.what());
+            return (NULL);
+        } catch (...) {
+            PyErr_SetString(getDataSourceException("Error"),
+                            "Unexpected exception");
+            return (NULL);
+        }
+    } else {
+        return (NULL);
+    }
+}
+
+PyObject*
+DataSourceClient_getUpdater(PyObject* po_self, PyObject* args) {
+    s_DataSourceClient* const self = static_cast<s_DataSourceClient*>(po_self);
+    PyObject *name_obj;
+    PyObject *replace_obj;
+    if (PyArg_ParseTuple(args, "O!O", &name_type, &name_obj, &replace_obj) &&
+        PyBool_Check(replace_obj)) {
+        bool replace = (replace_obj != Py_False);
+        try {
+            return (createZoneUpdaterObject(
+                        self->cppobj->getUpdater(PyName_ToName(name_obj),
+                                                 replace)));
+        } catch (const isc::NotImplemented& ne) {
+            PyErr_SetString(getDataSourceException("NotImplemented"),
+                            ne.what());
+            return (NULL);
+        } catch (const DataSourceError& dse) {
+            PyErr_SetString(getDataSourceException("Error"), dse.what());
+            return (NULL);
+        } catch (const std::exception& exc) {
+            PyErr_SetString(getDataSourceException("Error"), exc.what());
+            return (NULL);
+        } catch (...) {
+            PyErr_SetString(getDataSourceException("Error"),
+                            "Unexpected exception");
+            return (NULL);
+        }
+    } else {
+        return (NULL);
+    }
+}
+
+// This list contains the actual set of functions we have in
+// python. Each entry has
+// 1. Python method name
+// 2. Our static function here
+// 3. Argument type
+// 4. Documentation
+PyMethodDef DataSourceClient_methods[] = {
+    { "find_zone", reinterpret_cast<PyCFunction>(DataSourceClient_findZone),
+      METH_VARARGS, DataSourceClient_findZone_doc },
+    { "get_iterator",
+      reinterpret_cast<PyCFunction>(DataSourceClient_getIterator), METH_VARARGS,
+      DataSourceClient_getIterator_doc },
+    { "get_updater", reinterpret_cast<PyCFunction>(DataSourceClient_getUpdater),
+      METH_VARARGS, DataSourceClient_getUpdater_doc },
+    { NULL, NULL, 0, NULL }
+};
+
+int
+DataSourceClient_init(s_DataSourceClient* self, PyObject* args) {
+    // TODO: we should use the factory function which hasn't been written
+    // yet. For now we hardcode the sqlite3 initialization, and pass it one
+    // string for the database file. (similar to how the 'old direct'
+    // sqlite3_ds code works)
+    try {
+        char* db_file_name;
+        if (PyArg_ParseTuple(args, "s", &db_file_name)) {
+            boost::shared_ptr<DatabaseAccessor> sqlite3_accessor(
+                new SQLite3Accessor(db_file_name, isc::dns::RRClass::IN()));
+            self->cppobj = new DatabaseClient(isc::dns::RRClass::IN(),
+                                              sqlite3_accessor);
+            return (0);
+        } else {
+            return (-1);
+        }
+
+    } catch (const exception& ex) {
+        const string ex_what = "Failed to construct DataSourceClient object: " +
+            string(ex.what());
+        PyErr_SetString(getDataSourceException("Error"), ex_what.c_str());
+        return (-1);
+    } catch (...) {
+        PyErr_SetString(PyExc_RuntimeError,
+            "Unexpected exception in constructing DataSourceClient");
+        return (-1);
+    }
+    PyErr_SetString(PyExc_TypeError,
+                    "Invalid arguments to DataSourceClient constructor");
+
+    return (-1);
+}
+
+void
+DataSourceClient_destroy(s_DataSourceClient* const self) {
+    delete self->cppobj;
+    self->cppobj = NULL;
+    Py_TYPE(self)->tp_free(self);
+}
+
+} // end anonymous namespace
+
+namespace isc {
+namespace datasrc {
+namespace python {
+// This defines the complete type for reflection in python and
+// parsing of PyObject* to s_DataSourceClient
+// Most of the functions are not actually implemented and NULL here.
+PyTypeObject datasourceclient_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "datasrc.DataSourceClient",
+    sizeof(s_DataSourceClient),         // tp_basicsize
+    0,                                  // tp_itemsize
+    reinterpret_cast<destructor>(DataSourceClient_destroy),// tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash
+    NULL,                               // tp_call
+    NULL,                               // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    DataSourceClient_doc,
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    NULL,                               // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    DataSourceClient_methods,           // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    reinterpret_cast<initproc>(DataSourceClient_init),// tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
+
+} // namespace python
+} // namespace datasrc
+} // namespace isc
diff --git a/src/lib/python/isc/datasrc/client_python.h b/src/lib/python/isc/datasrc/client_python.h
new file mode 100644
index 0000000..b20fb6b
--- /dev/null
+++ b/src/lib/python/isc/datasrc/client_python.h
@@ -0,0 +1,35 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_DATASRC_CLIENT_H
+#define __PYTHON_DATASRC_CLIENT_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace datasrc {
+class DataSourceClient;
+
+namespace python {
+
+extern PyTypeObject datasourceclient_type;
+
+} // namespace python
+} // namespace datasrc
+} // namespace isc
+#endif // __PYTHON_DATASRC_CLIENT_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/python/isc/datasrc/datasrc.cc b/src/lib/python/isc/datasrc/datasrc.cc
new file mode 100644
index 0000000..4b0324a
--- /dev/null
+++ b/src/lib/python/isc/datasrc/datasrc.cc
@@ -0,0 +1,225 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#define PY_SSIZE_T_CLEAN
+#include <Python.h>
+#include <structmember.h>
+
+#include <config.h>
+
+#include <datasrc/client.h>
+#include <datasrc/database.h>
+#include <datasrc/sqlite3_accessor.h>
+
+#include "datasrc.h"
+#include "client_python.h"
+#include "finder_python.h"
+#include "iterator_python.h"
+#include "updater_python.h"
+
+#include <util/python/pycppwrapper_util.h>
+#include <dns/python/pydnspp_common.h>
+
+using namespace isc::datasrc;
+using namespace isc::datasrc::python;
+using namespace isc::util::python;
+using namespace isc::dns::python;
+
+namespace isc {
+namespace datasrc {
+namespace python {
+PyObject*
+getDataSourceException(const char* ex_name) {
+    PyObject* ex_obj = NULL;
+
+    PyObject* datasrc_module = PyImport_AddModule("isc.datasrc");
+    if (datasrc_module != NULL) {
+        PyObject* datasrc_dict = PyModule_GetDict(datasrc_module);
+        if (datasrc_dict != NULL) {
+            ex_obj = PyDict_GetItemString(datasrc_dict, ex_name);
+        }
+    }
+
+    if (ex_obj == NULL) {
+        ex_obj = PyExc_RuntimeError;
+    }
+    return (ex_obj);
+}
+
+} // end namespace python
+} // end namespace datasrc
+} // end namespace isc
+
+namespace {
+
+bool
+initModulePart_DataSourceClient(PyObject* mod) {
+    // We initialize the static description object with PyType_Ready(),
+    // then add it to the module. This is not just a check! (leaving
+    // this out results in segmentation faults)
+    if (PyType_Ready(&datasourceclient_type) < 0) {
+        return (false);
+    }
+    void* dscp = &datasourceclient_type;
+    if (PyModule_AddObject(mod, "DataSourceClient", static_cast<PyObject*>(dscp)) < 0) {
+        return (false);
+    }
+    Py_INCREF(&datasourceclient_type);
+
+    addClassVariable(datasourceclient_type, "SUCCESS",
+                     Py_BuildValue("I", result::SUCCESS));
+    addClassVariable(datasourceclient_type, "EXIST",
+                     Py_BuildValue("I", result::EXIST));
+    addClassVariable(datasourceclient_type, "NOTFOUND",
+                     Py_BuildValue("I", result::NOTFOUND));
+    addClassVariable(datasourceclient_type, "PARTIALMATCH",
+                     Py_BuildValue("I", result::PARTIALMATCH));
+
+    return (true);
+}
+
+bool
+initModulePart_ZoneFinder(PyObject* mod) {
+    // We initialize the static description object with PyType_Ready(),
+    // then add it to the module. This is not just a check! (leaving
+    // this out results in segmentation faults)
+    if (PyType_Ready(&zonefinder_type) < 0) {
+        return (false);
+    }
+    void* zip = &zonefinder_type;
+    if (PyModule_AddObject(mod, "ZoneFinder", static_cast<PyObject*>(zip)) < 0) {
+        return (false);
+    }
+    Py_INCREF(&zonefinder_type);
+
+    addClassVariable(zonefinder_type, "SUCCESS",
+                     Py_BuildValue("I", ZoneFinder::SUCCESS));
+    addClassVariable(zonefinder_type, "DELEGATION",
+                     Py_BuildValue("I", ZoneFinder::DELEGATION));
+    addClassVariable(zonefinder_type, "NXDOMAIN",
+                     Py_BuildValue("I", ZoneFinder::NXDOMAIN));
+    addClassVariable(zonefinder_type, "NXRRSET",
+                     Py_BuildValue("I", ZoneFinder::NXRRSET));
+    addClassVariable(zonefinder_type, "CNAME",
+                     Py_BuildValue("I", ZoneFinder::CNAME));
+    addClassVariable(zonefinder_type, "DNAME",
+                     Py_BuildValue("I", ZoneFinder::DNAME));
+
+    addClassVariable(zonefinder_type, "FIND_DEFAULT",
+                     Py_BuildValue("I", ZoneFinder::FIND_DEFAULT));
+    addClassVariable(zonefinder_type, "FIND_GLUE_OK",
+                     Py_BuildValue("I", ZoneFinder::FIND_GLUE_OK));
+    addClassVariable(zonefinder_type, "FIND_DNSSEC",
+                     Py_BuildValue("I", ZoneFinder::FIND_DNSSEC));
+
+
+    return (true);
+}
+
+bool
+initModulePart_ZoneIterator(PyObject* mod) {
+    // We initialize the static description object with PyType_Ready(),
+    // then add it to the module. This is not just a check! (leaving
+    // this out results in segmentation faults)
+    if (PyType_Ready(&zoneiterator_type) < 0) {
+        return (false);
+    }
+    void* zip = &zoneiterator_type;
+    if (PyModule_AddObject(mod, "ZoneIterator", static_cast<PyObject*>(zip)) < 0) {
+        return (false);
+    }
+    Py_INCREF(&zoneiterator_type);
+
+    return (true);
+}
+
+bool
+initModulePart_ZoneUpdater(PyObject* mod) {
+    // We initialize the static description object with PyType_Ready(),
+    // then add it to the module. This is not just a check! (leaving
+    // this out results in segmentation faults)
+    if (PyType_Ready(&zoneupdater_type) < 0) {
+        return (false);
+    }
+    void* zip = &zoneupdater_type;
+    if (PyModule_AddObject(mod, "ZoneUpdater", static_cast<PyObject*>(zip)) < 0) {
+        return (false);
+    }
+    Py_INCREF(&zoneupdater_type);
+
+    return (true);
+}
+
+
+PyObject* po_DataSourceError;
+PyObject* po_NotImplemented;
+
+PyModuleDef iscDataSrc = {
+    { PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
+    "datasrc",
+    "Python bindings for the classes in the isc::datasrc namespace.\n\n"
+    "These bindings are close match to the C++ API, but they are not complete "
+    "(some parts are not needed) and some are done in more python-like ways.",
+    -1,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL
+};
+
+} // end anonymous namespace
+
+PyMODINIT_FUNC
+PyInit_datasrc(void) {
+    PyObject* mod = PyModule_Create(&iscDataSrc);
+    if (mod == NULL) {
+        return (NULL);
+    }
+
+    if (!initModulePart_DataSourceClient(mod)) {
+        Py_DECREF(mod);
+        return (NULL);
+    }
+
+    if (!initModulePart_ZoneFinder(mod)) {
+        Py_DECREF(mod);
+        return (NULL);
+    }
+
+    if (!initModulePart_ZoneIterator(mod)) {
+        Py_DECREF(mod);
+        return (NULL);
+    }
+
+    if (!initModulePart_ZoneUpdater(mod)) {
+        Py_DECREF(mod);
+        return (NULL);
+    }
+
+    try {
+        po_DataSourceError = PyErr_NewException("isc.datasrc.Error", NULL,
+                                                NULL);
+        PyObjectContainer(po_DataSourceError).installToModule(mod, "Error");
+        po_NotImplemented = PyErr_NewException("isc.datasrc.NotImplemented",
+                                               NULL, NULL);
+        PyObjectContainer(po_NotImplemented).installToModule(mod,
+                                                             "NotImplemented");
+    } catch (...) {
+        Py_DECREF(mod);
+        return (NULL);
+    }
+
+    return (mod);
+}
diff --git a/src/lib/python/isc/datasrc/datasrc.h b/src/lib/python/isc/datasrc/datasrc.h
new file mode 100644
index 0000000..d82881b
--- /dev/null
+++ b/src/lib/python/isc/datasrc/datasrc.h
@@ -0,0 +1,50 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_DATASRC_H
+#define __PYTHON_DATASRC_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace datasrc {
+namespace python {
+
+// Return a Python exception object of the given name (ex_name) defined in
+// the isc.datasrc.datasrc loadable module.
+//
+// Since the datasrc module is a different binary image and is loaded separately
+// from the dns module, it would be very tricky to directly access to
+// C/C++ symbols defined in that module.  So we get access to these object
+// using the Python interpretor through this wrapper function.
+//
+// The __init__.py file should ensure isc.datasrc has been loaded by the time
+// whenever this function is called, and there shouldn't be any operation
+// within this function that can fail (such as dynamic memory allocation),
+// so this function should always succeed.  Yet there may be an overlooked
+// failure mode, perhaps due to a bug in the binding implementation, or
+// due to invalid usage.  As a last resort for such cases, this function
+// returns PyExc_RuntimeError (a C binding of Python's RuntimeError) should
+// it encounters an unexpected failure.
+extern PyObject* getDataSourceException(const char* ex_name);
+
+} // namespace python
+} // namespace datasrc
+} // namespace isc
+
+#endif // __PYTHON_ACL_DNS_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/python/isc/datasrc/finder_inc.cc b/src/lib/python/isc/datasrc/finder_inc.cc
new file mode 100644
index 0000000..2b47d02
--- /dev/null
+++ b/src/lib/python/isc/datasrc/finder_inc.cc
@@ -0,0 +1,96 @@
+namespace {
+const char* const ZoneFinder_doc = "\
+The base class to search a zone for RRsets.\n\
+\n\
+The ZoneFinder class is a wrapper for the c++ base class for representing an\n\
+object that performs DNS lookups in a specific zone accessible via a\n\
+data source. In general, different types of data sources (in-memory,\n\
+database-based, etc) define their own derived c++ classes of ZoneFinder,\n\
+implementing ways to retrieve the required data through the common\n\
+interfaces declared in the base class. Each concrete ZoneFinder object\n\
+is therefore (conceptually) associated with a specific zone of one\n\
+specific data source instance.\n\
+\n\
+The origin name and the RR class of the associated zone are available\n\
+via the get_origin() and get_class() methods, respectively.\n\
+\n\
+The most important method of this class is find(), which performs the\n\
+lookup for a given domain and type. See the description of the method\n\
+for details.\n\
+\n\
+It's not clear whether we should request that a zone finder form a\n\
+\"transaction\", that is, whether to ensure the finder is not\n\
+susceptible to changes made by someone else than the creator of the\n\
+finder. If we don't request that, for example, two different lookup\n\
+results for the same name and type can be different if other threads\n\
+or programs make updates to the zone between the lookups. We should\n\
+revisit this point as we gain more experiences.\n\
+\n\
+";
+
+const char* const ZoneFinder_getOrigin_doc = "\
+get_origin() -> isc.dns.Name\n\
+\n\
+Return the origin name of the zone.\n\
+\n\
+";
+
+const char* const ZoneFinder_getClass_doc = "\
+get_class() -> isc.dns.RRClass\n\
+\n\
+Return the RR class of the zone.\n\
+\n\
+";
+
+const char* const ZoneFinder_find_doc = "\
+find(name, type, target=NULL, options=FIND_DEFAULT) -> (code, FindResult)\n\
+\n\
+Search the zone for a given pair of domain name and RR type.\n\
+\n\
+- If the search name belongs under a zone cut, it returns the code of\n\
+  DELEGATION and the NS RRset at the zone cut.\n\
+- If there is no matching name, it returns the code of NXDOMAIN, and,\n\
+  if DNSSEC is requested, the NSEC RRset that proves the non-\n\
+  existence.\n\
+- If there is a matching name but no RRset of the search type, it\n\
+  returns the code of NXRRSET, and, if DNSSEC is required, the NSEC\n\
+  RRset for that name.\n\
+- If there is a CNAME RR of the searched name but there is no RR of\n\
+  the searched type of the name (so this type is different from\n\
+  CNAME), it returns the code of CNAME and that CNAME RR. Note that if\n\
+  the searched RR type is CNAME, it is considered a successful match,\n\
+  and the code of SUCCESS will be returned.\n\
+- If the search name matches a delegation point of DNAME, it returns\n\
+  the code of DNAME and that DNAME RR.\n\
+- If the target is a list, all RRsets under the domain are inserted\n\
+  there and SUCCESS (or NXDOMAIN, in case of empty domain) is returned\n\
+  instead of normall processing. This is intended to handle ANY query.\n\
+  : this behavior is controversial as we discussed in\n\
+  https://lists.isc.org/pipermail/bind10-dev/2011-January/001918.html\n\
+  We should revisit the interface before we heavily rely on it. The\n\
+  options parameter specifies customized behavior of the search. Their\n\
+  semantics is as follows:\n\
+  (This feature is disable at this time)\n\
+- GLUE_OK Allow search under a zone cut. By default the search will\n\
+  stop once it encounters a zone cut. If this option is specified it\n\
+  remembers information about the highest zone cut and continues the\n\
+  search until it finds an exact match for the given name or it\n\
+  detects there is no exact match. If an exact match is found, RRsets\n\
+  for that name are searched just like the normal case; otherwise, if\n\
+  the search has encountered a zone cut, DELEGATION with the\n\
+  information of the highest zone cut will be returned.\n\
+\n\
+This method raises an isc.datasrc.Error exception if there is an internal\n\
+error in the datasource.\n\
+\n\
+Parameters:\n\
+  name       The domain name to be searched for.\n\
+  type       The RR type to be searched for.\n\
+  target     If target is not NULL, insert all RRs under the domain\n\
+             into it.\n\
+  options    The search options.\n\
+\n\
+Return Value(s): A tuple of a result code an a FindResult object enclosing\n\
+the search result (see above).\n\
+";
+} // unnamed namespace
diff --git a/src/lib/python/isc/datasrc/finder_python.cc b/src/lib/python/isc/datasrc/finder_python.cc
new file mode 100644
index 0000000..598d300
--- /dev/null
+++ b/src/lib/python/isc/datasrc/finder_python.cc
@@ -0,0 +1,248 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// Enable this if you use s# variants with PyArg_ParseTuple(), see
+// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
+//#define PY_SSIZE_T_CLEAN
+
+// Python.h needs to be placed at the head of the program file, see:
+// http://docs.python.org/py3k/extending/extending.html#a-simple-example
+#include <Python.h>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <datasrc/client.h>
+#include <datasrc/database.h>
+#include <datasrc/data_source.h>
+#include <datasrc/sqlite3_accessor.h>
+#include <datasrc/iterator.h>
+#include <datasrc/zone.h>
+
+#include <dns/python/name_python.h>
+#include <dns/python/rrset_python.h>
+#include <dns/python/rrclass_python.h>
+#include <dns/python/rrtype_python.h>
+#include <dns/python/pydnspp_common.h>
+
+#include "datasrc.h"
+#include "finder_python.h"
+#include "finder_inc.cc"
+
+using namespace std;
+using namespace isc::util::python;
+using namespace isc::dns::python;
+using namespace isc::datasrc;
+using namespace isc::datasrc::python;
+
+namespace isc_datasrc_internal {
+// This is the shared code for the find() call in the finder and the updater
+// Is is intentionally not available through any header, nor at our standard
+// namespace, as it is not supposed to be called anywhere but from finder and
+// updater
+PyObject* ZoneFinder_helper(ZoneFinder* finder, PyObject* args) {
+    if (finder == NULL) {
+        PyErr_SetString(getDataSourceException("Error"),
+                        "Internal error in find() wrapper; finder object NULL");
+        return (NULL);
+    }
+    PyObject *name;
+    PyObject *rrtype;
+    PyObject *target;
+    int options_int;
+    if (PyArg_ParseTuple(args, "O!O!OI", &name_type, &name,
+                                         &rrtype_type, &rrtype,
+                                         &target, &options_int)) {
+        try {
+            ZoneFinder::FindOptions options =
+                static_cast<ZoneFinder::FindOptions>(options_int);
+            ZoneFinder::FindResult find_result(
+                finder->find(PyName_ToName(name),
+                                   PyRRType_ToRRType(rrtype),
+                                   NULL,
+                                   options
+                                   ));
+            ZoneFinder::Result r = find_result.code;
+            isc::dns::ConstRRsetPtr rrsp = find_result.rrset;
+            if (rrsp) {
+                // Use N instead of O so the refcount isn't increased twice
+                return (Py_BuildValue("IN", r, createRRsetObject(*rrsp)));
+            } else {
+                return (Py_BuildValue("IO", r, Py_None));
+            }
+        } catch (const DataSourceError& dse) {
+            PyErr_SetString(getDataSourceException("Error"), dse.what());
+            return (NULL);
+        } catch (const std::exception& exc) {
+            PyErr_SetString(getDataSourceException("Error"), exc.what());
+            return (NULL);
+        } catch (...) {
+            PyErr_SetString(getDataSourceException("Error"),
+                            "Unexpected exception");
+            return (NULL);
+        }
+    } else {
+        return (NULL);
+    }
+    return Py_BuildValue("I", 1);
+}
+
+} // end namespace internal
+
+namespace {
+// The s_* Class simply covers one instantiation of the object
+class s_ZoneFinder : public PyObject {
+public:
+    s_ZoneFinder() : cppobj(ZoneFinderPtr()) {};
+    ZoneFinderPtr cppobj;
+};
+
+// Shortcut type which would be convenient for adding class variables safely.
+typedef CPPPyObjectContainer<s_ZoneFinder, ZoneFinder> ZoneFinderContainer;
+
+// General creation and destruction
+int
+ZoneFinder_init(s_ZoneFinder* self, PyObject* args) {
+    // can't be called directly
+    PyErr_SetString(PyExc_TypeError,
+                    "ZoneFinder cannot be constructed directly");
+
+    return (-1);
+}
+
+void
+ZoneFinder_destroy(s_ZoneFinder* const self) {
+    // cppobj is a shared ptr, but to make sure things are not destroyed in
+    // the wrong order, we reset it here.
+    self->cppobj.reset();
+    Py_TYPE(self)->tp_free(self);
+}
+
+PyObject*
+ZoneFinder_getClass(PyObject* po_self, PyObject*) {
+    s_ZoneFinder* self = static_cast<s_ZoneFinder*>(po_self);
+    try {
+        return (createRRClassObject(self->cppobj->getClass()));
+    } catch (const std::exception& exc) {
+        PyErr_SetString(getDataSourceException("Error"), exc.what());
+        return (NULL);
+    }
+}
+
+PyObject*
+ZoneFinder_getOrigin(PyObject* po_self, PyObject*) {
+    s_ZoneFinder* self = static_cast<s_ZoneFinder*>(po_self);
+    try {
+        return (createNameObject(self->cppobj->getOrigin()));
+    } catch (const std::exception& exc) {
+        PyErr_SetString(getDataSourceException("Error"), exc.what());
+        return (NULL);
+    } catch (...) {
+        PyErr_SetString(getDataSourceException("Error"),
+                        "Unexpected exception");
+        return (NULL);
+    }
+}
+
+PyObject*
+ZoneFinder_find(PyObject* po_self, PyObject* args) {
+    s_ZoneFinder* const self = static_cast<s_ZoneFinder*>(po_self);
+    return (isc_datasrc_internal::ZoneFinder_helper(self->cppobj.get(), args));
+}
+
+// This list contains the actual set of functions we have in
+// python. Each entry has
+// 1. Python method name
+// 2. Our static function here
+// 3. Argument type
+// 4. Documentation
+PyMethodDef ZoneFinder_methods[] = {
+    { "get_origin", reinterpret_cast<PyCFunction>(ZoneFinder_getOrigin),
+      METH_NOARGS, ZoneFinder_getOrigin_doc },
+    { "get_class", reinterpret_cast<PyCFunction>(ZoneFinder_getClass),
+      METH_NOARGS, ZoneFinder_getClass_doc },
+    { "find", reinterpret_cast<PyCFunction>(ZoneFinder_find), METH_VARARGS,
+      ZoneFinder_find_doc },
+    { NULL, NULL, 0, NULL }
+};
+
+} // end of unnamed namespace
+
+namespace isc {
+namespace datasrc {
+namespace python {
+
+PyTypeObject zonefinder_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "datasrc.ZoneFinder",
+    sizeof(s_ZoneFinder),               // tp_basicsize
+    0,                                  // tp_itemsize
+    reinterpret_cast<destructor>(ZoneFinder_destroy),// tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash
+    NULL,                               // tp_call
+    NULL,                               // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    ZoneFinder_doc,
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    NULL,                               // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    ZoneFinder_methods,                 // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    reinterpret_cast<initproc>(ZoneFinder_init),// tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
+
+PyObject*
+createZoneFinderObject(isc::datasrc::ZoneFinderPtr source) {
+    s_ZoneFinder* py_zi = static_cast<s_ZoneFinder*>(
+        zonefinder_type.tp_alloc(&zonefinder_type, 0));
+    if (py_zi != NULL) {
+        py_zi->cppobj = source;
+    }
+    return (py_zi);
+}
+
+} // namespace python
+} // namespace datasrc
+} // namespace isc
+
diff --git a/src/lib/python/isc/datasrc/finder_python.h b/src/lib/python/isc/datasrc/finder_python.h
new file mode 100644
index 0000000..5f2404e
--- /dev/null
+++ b/src/lib/python/isc/datasrc/finder_python.h
@@ -0,0 +1,36 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_DATASRC_FINDER_H
+#define __PYTHON_DATASRC_FINDER_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace datasrc {
+
+namespace python {
+
+extern PyTypeObject zonefinder_type;
+
+PyObject* createZoneFinderObject(isc::datasrc::ZoneFinderPtr source);
+
+} // namespace python
+} // namespace datasrc
+} // namespace isc
+#endif // __PYTHON_DATASRC_FINDER_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/python/isc/datasrc/iterator_inc.cc b/src/lib/python/isc/datasrc/iterator_inc.cc
new file mode 100644
index 0000000..b1d9d25
--- /dev/null
+++ b/src/lib/python/isc/datasrc/iterator_inc.cc
@@ -0,0 +1,34 @@
+namespace {
+
+const char* const ZoneIterator_doc = "\
+Read-only iterator to a zone.\n\
+\n\
+You can get an instance of the ZoneIterator from\n\
+DataSourceClient.get_iterator() method. The actual concrete\n\
+c++ implementation will be different depending on the actual data source\n\
+used. This is the abstract interface.\n\
+\n\
+There's no way to start iterating from the beginning again or return.\n\
+\n\
+The ZoneIterator is a python iterator, and can be iterated over directly.\n\
+";
+
+const char* const ZoneIterator_getNextRRset_doc = "\
+get_next_rrset() -> isc.dns.RRset\n\
+\n\
+Get next RRset from the zone.\n\
+\n\
+This returns the next RRset in the zone.\n\
+\n\
+Any special order is not guaranteed.\n\
+\n\
+While this can potentially throw anything (including standard\n\
+allocation errors), it should be rare.\n\
+\n\
+Pointer to the next RRset or None pointer when the iteration gets to\n\
+the end of the zone.\n\
+\n\
+Raises an isc.datasrc.Error exception if it is called again after returning\n\
+None\n\
+";
+} // unnamed namespace
diff --git a/src/lib/python/isc/datasrc/iterator_python.cc b/src/lib/python/isc/datasrc/iterator_python.cc
new file mode 100644
index 0000000..b482ea6
--- /dev/null
+++ b/src/lib/python/isc/datasrc/iterator_python.cc
@@ -0,0 +1,202 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// Enable this if you use s# variants with PyArg_ParseTuple(), see
+// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
+//#define PY_SSIZE_T_CLEAN
+
+// Python.h needs to be placed at the head of the program file, see:
+// http://docs.python.org/py3k/extending/extending.html#a-simple-example
+#include <Python.h>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <datasrc/client.h>
+#include <datasrc/database.h>
+#include <datasrc/sqlite3_accessor.h>
+#include <datasrc/iterator.h>
+
+#include <dns/python/name_python.h>
+#include <dns/python/rrset_python.h>
+
+#include "datasrc.h"
+#include "iterator_python.h"
+
+#include "iterator_inc.cc"
+
+using namespace std;
+using namespace isc::util::python;
+using namespace isc::dns::python;
+using namespace isc::datasrc;
+using namespace isc::datasrc::python;
+
+namespace {
+// The s_* Class simply covers one instantiation of the object
+class s_ZoneIterator : public PyObject {
+public:
+    s_ZoneIterator() : cppobj(ZoneIteratorPtr()) {};
+    ZoneIteratorPtr cppobj;
+};
+
+// Shortcut type which would be convenient for adding class variables safely.
+typedef CPPPyObjectContainer<s_ZoneIterator, ZoneIterator>
+    ZoneIteratorContainer;
+
+// General creation and destruction
+int
+ZoneIterator_init(s_ZoneIterator* self, PyObject* args) {
+    // can't be called directly
+    PyErr_SetString(PyExc_TypeError,
+                    "ZoneIterator cannot be constructed directly");
+
+    return (-1);
+}
+
+void
+ZoneIterator_destroy(s_ZoneIterator* const self) {
+    // cppobj is a shared ptr, but to make sure things are not destroyed in
+    // the wrong order, we reset it here.
+    self->cppobj.reset();
+    Py_TYPE(self)->tp_free(self);
+}
+
+//
+// We declare the functions here, the definitions are below
+// the type definition of the object, since both can use the other
+//
+PyObject*
+ZoneIterator_getNextRRset(PyObject* po_self, PyObject*) {
+    s_ZoneIterator* self = static_cast<s_ZoneIterator*>(po_self);
+    if (!self->cppobj) {
+        PyErr_SetString(getDataSourceException("Error"),
+                        "get_next_rrset() called past end of iterator");
+        return (NULL);
+    }
+    try {
+        isc::dns::ConstRRsetPtr rrset = self->cppobj->getNextRRset();
+        if (!rrset) {
+            Py_RETURN_NONE;
+        }
+        return (createRRsetObject(*rrset));
+    } catch (const isc::Exception& isce) {
+        // isc::Unexpected is thrown when we call getNextRRset() when we are
+        // already done iterating ('iterating past end')
+        // We could also simply return None again
+        PyErr_SetString(getDataSourceException("Error"), isce.what());
+        return (NULL);
+    } catch (const std::exception& exc) {
+        PyErr_SetString(getDataSourceException("Error"), exc.what());
+        return (NULL);
+    } catch (...) {
+        PyErr_SetString(getDataSourceException("Error"),
+                        "Unexpected exception");
+        return (NULL);
+    }
+}
+
+PyObject*
+ZoneIterator_iter(PyObject *self) {
+    Py_INCREF(self);
+    return (self);
+}
+
+PyObject*
+ZoneIterator_next(PyObject* self) {
+    PyObject *result = ZoneIterator_getNextRRset(self, NULL);
+    // iter_next must return NULL without error instead of Py_None
+    if (result == Py_None) {
+        Py_DECREF(result);
+        return (NULL);
+    } else {
+        return (result);
+    }
+}
+
+PyMethodDef ZoneIterator_methods[] = {
+    { "get_next_rrset",
+      reinterpret_cast<PyCFunction>(ZoneIterator_getNextRRset), METH_NOARGS,
+      ZoneIterator_getNextRRset_doc },
+    { NULL, NULL, 0, NULL }
+};
+
+
+} // end of unnamed namespace
+
+namespace isc {
+namespace datasrc {
+namespace python {
+PyTypeObject zoneiterator_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "datasrc.ZoneIterator",
+    sizeof(s_ZoneIterator),             // tp_basicsize
+    0,                                  // tp_itemsize
+    reinterpret_cast<destructor>(ZoneIterator_destroy),// tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash
+    NULL,                               // tp_call
+    NULL,                               // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    ZoneIterator_doc,
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    NULL,                               // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    ZoneIterator_iter,                  // tp_iter
+    ZoneIterator_next,                  // tp_iternext
+    ZoneIterator_methods,               // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    reinterpret_cast<initproc>(ZoneIterator_init),// tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
+
+PyObject*
+createZoneIteratorObject(isc::datasrc::ZoneIteratorPtr source) {
+    s_ZoneIterator* py_zi = static_cast<s_ZoneIterator*>(
+        zoneiterator_type.tp_alloc(&zoneiterator_type, 0));
+    if (py_zi != NULL) {
+        py_zi->cppobj = source;
+    }
+    return (py_zi);
+}
+
+} // namespace python
+} // namespace datasrc
+} // namespace isc
+
diff --git a/src/lib/python/isc/datasrc/iterator_python.h b/src/lib/python/isc/datasrc/iterator_python.h
new file mode 100644
index 0000000..b457740
--- /dev/null
+++ b/src/lib/python/isc/datasrc/iterator_python.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_DATASRC_ITERATOR_H
+#define __PYTHON_DATASRC_ITERATOR_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace datasrc {
+class DataSourceClient;
+
+namespace python {
+
+extern PyTypeObject zoneiterator_type;
+
+PyObject* createZoneIteratorObject(isc::datasrc::ZoneIteratorPtr source);
+
+
+} // namespace python
+} // namespace datasrc
+} // namespace isc
+#endif // __PYTHON_DATASRC_ITERATOR_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/python/isc/datasrc/tests/Makefile.am b/src/lib/python/isc/datasrc/tests/Makefile.am
index 6f6d157..be30dfa 100644
--- a/src/lib/python/isc/datasrc/tests/Makefile.am
+++ b/src/lib/python/isc/datasrc/tests/Makefile.am
@@ -1,16 +1,18 @@
 PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
-PYTESTS = master_test.py sqlite3_ds_test.py
+# old tests, TODO remove or change to use new API?
+#PYTESTS = master_test.py sqlite3_ds_test.py
+PYTESTS =  datasrc_test.py
 EXTRA_DIST = $(PYTESTS)
 
 EXTRA_DIST += testdata/brokendb.sqlite3
 EXTRA_DIST += testdata/example.com.sqlite3
-CLEANFILES = $(abs_builddir)/example.com.out.sqlite3
+CLEANFILES = $(abs_builddir)/rwtest.sqlite3.copied
 
 # If necessary (rare cases), explicitly specify paths to dynamic libraries
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
@@ -23,7 +25,7 @@ endif
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	$(LIBRARY_PATH_PLACEHOLDER) \
-	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/python/isc/log \
+	PYTHONPATH=:$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/python/isc/log:$(abs_top_builddir)/src/lib/python/isc/datasrc/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs \
 	TESTDATA_PATH=$(abs_srcdir)/testdata \
 	TESTDATA_WRITE_PATH=$(abs_builddir) \
 	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
diff --git a/src/lib/python/isc/datasrc/tests/datasrc_test.py b/src/lib/python/isc/datasrc/tests/datasrc_test.py
new file mode 100644
index 0000000..15ceb80
--- /dev/null
+++ b/src/lib/python/isc/datasrc/tests/datasrc_test.py
@@ -0,0 +1,389 @@
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import isc.log
+import isc.datasrc
+import isc.dns
+import unittest
+import os
+import shutil
+
+TESTDATA_PATH = os.environ['TESTDATA_PATH'] + os.sep
+TESTDATA_WRITE_PATH = os.environ['TESTDATA_WRITE_PATH'] + os.sep
+
+READ_ZONE_DB_FILE = TESTDATA_PATH + "example.com.sqlite3"
+BROKEN_DB_FILE = TESTDATA_PATH + "brokendb.sqlite3"
+WRITE_ZONE_DB_FILE = TESTDATA_WRITE_PATH + "rwtest.sqlite3.copied"
+NEW_DB_FILE = TESTDATA_WRITE_PATH + "new_db.sqlite3"
+
+def add_rrset(rrset_list, name, rrclass, rrtype, ttl, rdatas):
+    rrset_to_add = isc.dns.RRset(name, rrclass, rrtype, ttl)
+    if rdatas is not None:
+        for rdata in rdatas:
+            rrset_to_add.add_rdata(isc.dns.Rdata(rrtype, rrclass, rdata))
+    rrset_list.append(rrset_to_add)
+
+# helper function, we have no direct rrset comparison atm
+def rrsets_equal(a, b):
+    # no accessor for sigs either (so this only checks name, class, type, ttl,
+    # and rdata)
+    # also, because of the fake data in rrsigs, if the type is rrsig, the
+    # rdata is not checked
+    return a.get_name() == b.get_name() and\
+           a.get_class() == b.get_class() and\
+           a.get_type() == b.get_type() and \
+           a.get_ttl() == b.get_ttl() and\
+           (a.get_type() == isc.dns.RRType.RRSIG() or
+            sorted(a.get_rdata()) == sorted(b.get_rdata()))
+
+# returns true if rrset is in expected_rrsets
+# will remove the rrset from expected_rrsets if found
+def check_for_rrset(expected_rrsets, rrset):
+    for cur_rrset in expected_rrsets[:]:
+        if rrsets_equal(cur_rrset, rrset):
+            expected_rrsets.remove(cur_rrset)
+            return True
+    return False
+
+class DataSrcClient(unittest.TestCase):
+
+    def test_construct(self):
+        # can't construct directly
+        self.assertRaises(TypeError, isc.datasrc.ZoneIterator)
+
+
+    def test_iterate(self):
+        dsc = isc.datasrc.DataSourceClient(READ_ZONE_DB_FILE)
+
+        # for RRSIGS, the TTL's are currently modified. This test should
+        # start failing when we fix that.
+        rrs = dsc.get_iterator(isc.dns.Name("sql1.example.com."))
+
+        # we do not know the order in which they are returned by the iterator
+        # but we do want to check them, so we put all records into one list
+        # sort it (doesn't matter which way it is sorted, as long as it is
+        # sorted)
+
+        # RRset is (atm) an unorderable type, and within an rrset, the
+        # rdatas and rrsigs may also be in random order. In theory the
+        # rrsets themselves can be returned in any order.
+        #
+        # So we create a second list with all rrsets we expect, and for each
+        # rrset we get from the iterator, see if it is in that list, and
+        # remove it.
+        #
+        # When the iterator is empty, we check no rrsets are left in the
+        # list of expected ones
+        expected_rrset_list = []
+
+        name = isc.dns.Name("sql1.example.com")
+        rrclass = isc.dns.RRClass.IN()
+        add_rrset(expected_rrset_list, name, rrclass,
+                  isc.dns.RRType.DNSKEY(), isc.dns.RRTTL(3600),
+                  [
+                     "256 3 5 AwEAAdYdRhBAEY67R/8G1N5AjGF6asIiNh/pNGeQ8xDQP13J"+
+                     "N2lo+sNqWcmpYNhuVqRbLB+mamsU1XcCICSBvAlSmfz/ZUdafX23knAr"+
+                     "TlALxMmspcfdpqun3Yr3YYnztuj06rV7RqmveYckWvAUXVYMSMQZfJ30"+
+                     "5fs0dE/xLztL/CzZ",
+                     "257 3 5 AwEAAbaKDSa9XEFTsjSYpUTHRotTS9Tz3krfDucugW5UokGQ"+
+                     "KC26QlyHXlPTZkC+aRFUs/dicJX2kopndLcnlNAPWiKnKtrsFSCnIJDB"+
+                     "ZIyvcKq+9RXmV3HK3bUdHnQZ88IZWBRmWKfZ6wnzHo53kdYKAemTErkz"+
+                     "taX3lRRPLYWpxRcDPEjysXT3Lh0vfL5D+CIO1yKw/q7C+v6+/kYAxc2l"+
+                     "fbNE3HpklSuF+dyX4nXxWgzbcFuLz5Bwfq6ZJ9RYe/kNkA0uMWNa1KkG"+
+                     "eRh8gg22kgD/KT5hPTnpezUWLvoY5Qc7IB3T0y4n2JIwiF2ZrZYVrWgD"+
+                     "jRWAzGsxJiJyjd6w2k0="
+                  ])
+        add_rrset(expected_rrset_list, name, rrclass,
+                  isc.dns.RRType.NS(), isc.dns.RRTTL(3600),
+                  [
+                    "dns01.example.com.",
+                    "dns02.example.com.",
+                    "dns03.example.com."
+                  ])
+        add_rrset(expected_rrset_list, name, rrclass,
+                  isc.dns.RRType.NSEC(), isc.dns.RRTTL(7200),
+                  [
+                     "www.sql1.example.com. NS SOA RRSIG NSEC DNSKEY"
+                  ])
+        # For RRSIGS, we can't add the fake data through the API, so we
+        # simply pass no rdata at all (which is skipped by the check later)
+        add_rrset(expected_rrset_list, name, rrclass,
+                  isc.dns.RRType.RRSIG(), isc.dns.RRTTL(3600), None)
+        add_rrset(expected_rrset_list, name, rrclass,
+                  isc.dns.RRType.SOA(), isc.dns.RRTTL(3600),
+                  [
+                     "master.example.com. admin.example.com. 678 3600 1800 2419200 7200"
+                  ])
+        name = isc.dns.Name("www.sql1.example.com.")
+        add_rrset(expected_rrset_list, name, rrclass,
+                  isc.dns.RRType.A(), isc.dns.RRTTL(3600),
+                  [
+                     "192.0.2.100"
+                  ])
+        name = isc.dns.Name("www.sql1.example.com.")
+        add_rrset(expected_rrset_list, name, rrclass,
+                  isc.dns.RRType.NSEC(), isc.dns.RRTTL(7200),
+                  [
+                     "sql1.example.com. A RRSIG NSEC"
+                  ])
+        add_rrset(expected_rrset_list, name, rrclass,
+                  isc.dns.RRType.RRSIG(), isc.dns.RRTTL(3600), None)
+
+        # rrs is an iterator, but also has direct get_next_rrset(), use
+        # the latter one here
+        rrset_to_check = rrs.get_next_rrset()
+        while (rrset_to_check != None):
+            self.assertTrue(check_for_rrset(expected_rrset_list,
+                                            rrset_to_check),
+                            "Unexpected rrset returned by iterator:\n" +
+                            rrset_to_check.to_text())
+            rrset_to_check = rrs.get_next_rrset()
+
+        # Now check there are none left
+        self.assertEqual(0, len(expected_rrset_list),
+                         "RRset(s) not returned by iterator: " +
+                         str([rrset.to_text() for rrset in expected_rrset_list ]
+                        ))
+
+        # TODO should we catch this (iterating past end) and just return None
+        # instead of failing?
+        self.assertRaises(isc.datasrc.Error, rrs.get_next_rrset)
+
+        rrets = dsc.get_iterator(isc.dns.Name("example.com"))
+        # there are more than 80 RRs in this zone... let's just count them
+        # (already did a full check of the smaller zone above)
+        self.assertEqual(55, len(list(rrets)))
+        # TODO should we catch this (iterating past end) and just return None
+        # instead of failing?
+        self.assertRaises(isc.datasrc.Error, rrs.get_next_rrset)
+
+        self.assertRaises(TypeError, dsc.get_iterator, "asdf")
+
+    def test_construct(self):
+        # can't construct directly
+        self.assertRaises(TypeError, isc.datasrc.ZoneFinder)
+
+    def test_find(self):
+        dsc = isc.datasrc.DataSourceClient(READ_ZONE_DB_FILE)
+
+        result, finder = dsc.find_zone(isc.dns.Name("example.com"))
+        self.assertEqual(finder.SUCCESS, result)
+        self.assertEqual(isc.dns.RRClass.IN(), finder.get_class())
+        self.assertEqual("example.com.", finder.get_origin().to_text())
+
+        result, rrset = finder.find(isc.dns.Name("www.example.com"),
+                                    isc.dns.RRType.A(),
+                                    None,
+                                    finder.FIND_DEFAULT)
+        self.assertEqual(finder.SUCCESS, result)
+        self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
+                         rrset.to_text())
+
+        result, rrset = finder.find(isc.dns.Name("www.sql1.example.com"),
+                                    isc.dns.RRType.A(),
+                                    None,
+                                    finder.FIND_DEFAULT)
+        self.assertEqual(finder.DELEGATION, result)
+        self.assertEqual("sql1.example.com. 3600 IN NS dns01.example.com.\n" +
+                         "sql1.example.com. 3600 IN NS dns02.example.com.\n" +
+                         "sql1.example.com. 3600 IN NS dns03.example.com.\n",
+                         rrset.to_text())
+
+        result, rrset = finder.find(isc.dns.Name("doesnotexist.example.com"),
+                                    isc.dns.RRType.A(),
+                                    None,
+                                    finder.FIND_DEFAULT)
+        self.assertEqual(finder.NXDOMAIN, result)
+        self.assertEqual(None, rrset)
+
+        result, rrset = finder.find(isc.dns.Name("www.some.other.domain"),
+                                    isc.dns.RRType.A(),
+                                    None,
+                                    finder.FIND_DEFAULT)
+        self.assertEqual(finder.NXDOMAIN, result)
+        self.assertEqual(None, rrset)
+
+        result, rrset = finder.find(isc.dns.Name("www.example.com"),
+                                    isc.dns.RRType.TXT(),
+                                    None,
+                                    finder.FIND_DEFAULT)
+        self.assertEqual(finder.NXRRSET, result)
+        self.assertEqual(None, rrset)
+
+        result, rrset = finder.find(isc.dns.Name("cname-ext.example.com"),
+                                    isc.dns.RRType.A(),
+                                    None,
+                                    finder.FIND_DEFAULT)
+        self.assertEqual(finder.CNAME, result)
+        self.assertEqual(
+            "cname-ext.example.com. 3600 IN CNAME www.sql1.example.com.\n",
+            rrset.to_text())
+
+        self.assertRaises(TypeError, finder.find,
+                          "foo",
+                          isc.dns.RRType.A(),
+                          None,
+                          finder.FIND_DEFAULT)
+        self.assertRaises(TypeError, finder.find,
+                          isc.dns.Name("cname-ext.example.com"),
+                          "foo",
+                          None,
+                          finder.FIND_DEFAULT)
+        self.assertRaises(TypeError, finder.find,
+                          isc.dns.Name("cname-ext.example.com"),
+                          isc.dns.RRType.A(),
+                          None,
+                          "foo")
+
+
+class DataSrcUpdater(unittest.TestCase):
+
+    def setUp(self):
+        # Make a fresh copy of the writable database with all original content
+        shutil.copyfile(READ_ZONE_DB_FILE, WRITE_ZONE_DB_FILE)
+
+    def test_construct(self):
+        # can't construct directly
+        self.assertRaises(TypeError, isc.datasrc.ZoneUpdater)
+
+    def test_update_delete_commit(self):
+
+        dsc = isc.datasrc.DataSourceClient(WRITE_ZONE_DB_FILE)
+
+        # first make sure, through a separate finder, that some record exists
+        result, finder = dsc.find_zone(isc.dns.Name("example.com"))
+        self.assertEqual(finder.SUCCESS, result)
+        self.assertEqual(isc.dns.RRClass.IN(), finder.get_class())
+        self.assertEqual("example.com.", finder.get_origin().to_text())
+
+        result, rrset = finder.find(isc.dns.Name("www.example.com"),
+                                    isc.dns.RRType.A(),
+                                    None,
+                                    finder.FIND_DEFAULT)
+        self.assertEqual(finder.SUCCESS, result)
+        self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
+                         rrset.to_text())
+
+        rrset_to_delete = rrset;
+
+        # can't delete rrset with associated sig. Abuse that to force an
+        # exception first, then remove the sig, then delete the record
+        updater = dsc.get_updater(isc.dns.Name("example.com"), True)
+        self.assertRaises(isc.datasrc.Error, updater.delete_rrset,
+                          rrset_to_delete)
+
+        rrset_to_delete.remove_rrsig()
+
+        updater.delete_rrset(rrset_to_delete)
+
+        # The record should be gone in the updater, but not in the original
+        # finder (since we have not committed)
+        result, rrset = updater.find(isc.dns.Name("www.example.com"),
+                                     isc.dns.RRType.A(),
+                                     None,
+                                     finder.FIND_DEFAULT)
+        self.assertEqual(finder.NXDOMAIN, result)
+        self.assertEqual(None, rrset)
+
+        result, rrset = finder.find(isc.dns.Name("www.example.com"),
+                                    isc.dns.RRType.A(),
+                                    None,
+                                    finder.FIND_DEFAULT)
+        self.assertEqual(finder.SUCCESS, result)
+        self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
+                         rrset.to_text())
+
+        updater.commit()
+        # second commit should raise exception
+        self.assertRaises(isc.datasrc.Error, updater.commit)
+
+        # the record should be gone now in the 'real' finder as well
+        result, rrset = finder.find(isc.dns.Name("www.example.com"),
+                                    isc.dns.RRType.A(),
+                                    None,
+                                    finder.FIND_DEFAULT)
+        self.assertEqual(finder.NXDOMAIN, result)
+        self.assertEqual(None, rrset)
+
+        # now add it again
+        updater = dsc.get_updater(isc.dns.Name("example.com"), True)
+        updater.add_rrset(rrset_to_delete)
+        updater.commit()
+
+        # second commit should throw
+        self.assertRaises(isc.datasrc.Error, updater.commit)
+
+        result, rrset = finder.find(isc.dns.Name("www.example.com"),
+                                    isc.dns.RRType.A(),
+                                    None,
+                                    finder.FIND_DEFAULT)
+        self.assertEqual(finder.SUCCESS, result)
+        self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
+                         rrset.to_text())
+
+    def test_update_delete_abort(self):
+        dsc = isc.datasrc.DataSourceClient(WRITE_ZONE_DB_FILE)
+
+        # first make sure, through a separate finder, that some record exists
+        result, finder = dsc.find_zone(isc.dns.Name("example.com"))
+        self.assertEqual(finder.SUCCESS, result)
+        self.assertEqual(isc.dns.RRClass.IN(), finder.get_class())
+        self.assertEqual("example.com.", finder.get_origin().to_text())
+
+        result, rrset = finder.find(isc.dns.Name("www.example.com"),
+                                    isc.dns.RRType.A(),
+                                    None,
+                                    finder.FIND_DEFAULT)
+        self.assertEqual(finder.SUCCESS, result)
+        self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
+                         rrset.to_text())
+
+        rrset_to_delete = rrset;
+
+        # can't delete rrset with associated sig. Abuse that to force an
+        # exception first, then remove the sig, then delete the record
+        updater = dsc.get_updater(isc.dns.Name("example.com"), True)
+        self.assertRaises(isc.datasrc.Error, updater.delete_rrset,
+                          rrset_to_delete)
+
+        rrset_to_delete.remove_rrsig()
+
+        updater.delete_rrset(rrset_to_delete)
+
+        # The record should be gone in the updater, but not in the original
+        # finder (since we have not committed)
+        result, rrset = updater.find(isc.dns.Name("www.example.com"),
+                                     isc.dns.RRType.A(),
+                                     None,
+                                     finder.FIND_DEFAULT)
+        self.assertEqual(finder.NXDOMAIN, result)
+        self.assertEqual(None, rrset)
+
+        # destroy the updater, which should make it roll back
+        updater = None
+
+        # the record should still be available in the 'real' finder as well
+        result, rrset = finder.find(isc.dns.Name("www.example.com"),
+                                    isc.dns.RRType.A(),
+                                    None,
+                                    finder.FIND_DEFAULT)
+        self.assertEqual(finder.SUCCESS, result)
+        self.assertEqual("www.example.com. 3600 IN A 192.0.2.1\n",
+                         rrset.to_text())
+
+
+if __name__ == "__main__":
+    isc.log.init("bind10")
+    unittest.main()
diff --git a/src/lib/python/isc/datasrc/updater_inc.cc b/src/lib/python/isc/datasrc/updater_inc.cc
new file mode 100644
index 0000000..32715ec
--- /dev/null
+++ b/src/lib/python/isc/datasrc/updater_inc.cc
@@ -0,0 +1,181 @@
+namespace {
+
+const char* const ZoneUpdater_doc = "\
+The base class to make updates to a single zone.\n\
+\n\
+On construction, each derived class object will start a\n\
+\"transaction\" for making updates to a specific zone (this means a\n\
+constructor of a derived class would normally take parameters to\n\
+identify the zone to be updated). The underlying realization of a\n\
+\"transaction\" will differ for different derived classes; if it uses\n\
+a general purpose database as a backend, it will involve performing\n\
+some form of \"begin transaction\" statement for the database.\n\
+\n\
+Updates (adding or deleting RRs) are made via add_rrset() and\n\
+delete_rrset() methods. Until the commit() method is called the\n\
+changes are local to the updater object. For example, they won't be\n\
+visible via a ZoneFinder object, but only by the updater's own find()\n\
+method. The commit() completes the transaction and makes the changes\n\
+visible to others.\n\
+\n\
+This class does not provide an explicit \"rollback\" interface. If\n\
+something wrong or unexpected happens during the updates and the\n\
+caller wants to cancel the intermediate updates, the caller should\n\
+simply destroy the updater object without calling commit(). The\n\
+destructor is supposed to perform the \"rollback\" operation,\n\
+depending on the internal details of the derived class.\n\
+\n\
+This initial implementation provides a quite simple interface of\n\
+adding and deleting RRs (see the description of the related methods).\n\
+It may be revisited as we gain more experiences.\n\
+\n\
+";
+
+const char* const ZoneUpdater_addRRset_doc = "\
+add_rrset(rrset) -> No return value\n\
+\n\
+Add an RRset to a zone via the updater.\n\
+It performs a few basic checks:\n\
+- Whether the RR class is identical to that for the zone to be updated\n\
+- Whether the RRset is not empty, i.e., it has at least one RDATA\n\
+- Whether the RRset is not associated with an RRSIG, i.e., whether\n\
+  get_rrsig() on the RRset returns a NULL pointer.\n\
+\n\
+and otherwise does not check any oddity. For example, it doesn't check\n\
+whether the owner name of the specified RRset is a subdomain of the\n\
+zone's origin; it doesn't care whether or not there is already an\n\
+RRset of the same name and RR type in the zone, and if there is,\n\
+whether any of the existing RRs have duplicate RDATA with the added\n\
+ones. If these conditions matter the calling application must examine\n\
+the existing data beforehand using the ZoneFinder returned by\n\
+get_finder().\n\
+\n\
+The validation requirement on the associated RRSIG is temporary. If we\n\
+find it more reasonable and useful to allow adding a pair of RRset and\n\
+its RRSIG RRset as we gain experiences with the interface, we may\n\
+remove this restriction. Until then we explicitly check it to prevent\n\
+accidental misuse.\n\
+\n\
+Conceptually, on successful call to this method, the zone will have\n\
+the specified RRset, and if there is already an RRset of the same name\n\
+and RR type, these two sets will be \"merged\". \"Merged\" means that\n\
+a subsequent call to ZoneFinder.find() for the name and type will\n\
+result in success and the returned RRset will contain all previously\n\
+existing and newly added RDATAs with the TTL being the minimum of the\n\
+two RRsets. The underlying representation of the \"merged\" RRsets may\n\
+vary depending on the characteristic of the underlying data source.\n\
+For example, if it uses a general purpose database that stores each RR\n\
+of the same RRset separately, it may simply be a larger sets of RRs\n\
+based on both the existing and added RRsets; the TTLs of the RRs may\n\
+be different within the database, and there may even be duplicate RRs\n\
+in different database rows. As long as the RRset returned via\n\
+ZoneFinder.find() conforms to the concept of \"merge\", the actual\n\
+internal representation is up to the implementation.\n\
+\n\
+This method must not be called once commit() is performed. If it calls\n\
+after commit() the implementation must throw a isc.datasrc.Error\n\
+exception.\n\
+\n\
+Todo As noted above we may have to revisit the design details as we\n\
+gain experiences:\n\
+\n\
+- we may want to check (and maybe reject) if there is already a\n\
+  duplicate RR (that has the same RDATA).\n\
+- we may want to check (and maybe reject) if there is already an RRset\n\
+  of the same name and RR type with different TTL\n\
+- we may even want to check if there is already any RRset of the same\n\
+  name and RR type.\n\
+- we may want to add an \"options\" parameter that can control the\n\
+  above points\n\
+- we may want to have this method return a value containing the\n\
+  information on whether there's a duplicate, etc.\n\
+\n\
+Exceptions:\n\
+  isc.datasrc.Error Called after commit(), RRset is invalid (see above),\n\
+                    internal data source error, or wrapper error\n\
+\n\
+Parameters:\n\
+  rrset      The RRset to be added\n\
+\n\
+";
+
+const char* const ZoneUpdater_deleteRRset_doc = "\
+delete_rrset(rrset) -> No return value\n\
+\n\
+Delete an RRset from a zone via the updater.\n\
+\n\
+Like add_rrset(), the detailed semantics and behavior of this method\n\
+may have to be revisited in a future version. The following are based\n\
+on the initial implementation decisions.\n\
+\n\
+- Existing RRs that don't match any of the specified RDATAs will\n\
+  remain in the zone.\n\
+- Any RRs of the specified RRset that doesn't exist in the zone will\n\
+  simply be ignored; the implementation of this method is not supposed\n\
+  to check that condition.\n\
+- The TTL of the RRset is ignored; matching is only performed by the\n\
+  owner name, RR type and RDATA\n\
+\n\
+Ignoring the TTL may not look sensible, but it's based on the\n\
+observation that it will result in more intuitive result, especially\n\
+when the underlying data source is a general purpose database. See\n\
+also the c++ documentation of DatabaseAccessor::DeleteRecordInZone()\n\
+on this point. It also matches the dynamic update protocol (RFC2136),\n\
+where TTLs are ignored when deleting RRs.\n\
+\n\
+This method performs a limited level of validation on the specified\n\
+RRset:\n\
+- Whether the RR class is identical to that for the zone to be updated\n\
+- Whether the RRset is not empty, i.e., it has at least one RDATA\n\
+- Whether the RRset is not associated with an RRSIG\n\
+\n\
+This method must not be called once commit() is performed. If it calls\n\
+after commit() the implementation must throw a isc.datasrc.Error\n\
+exception.\n\
+\n\
+Todo: As noted above we may have to revisit the design details as we\n\
+gain experiences:\n\
+\n\
+- we may want to check (and maybe reject) if some or all of the RRs\n\
+  for the specified RRset don't exist in the zone\n\
+- we may want to allow an option to \"delete everything\" for\n\
+  specified name and/or specified name + RR type.\n\
+- as mentioned above, we may want to include the TTL in matching the\n\
+  deleted RRs\n\
+- we may want to add an \"options\" parameter that can control the\n\
+  above points\n\
+- we may want to have this method return a value containing the\n\
+  information on whether there's any RRs that are specified but don't\n\
+  exit, the number of actually deleted RRs, etc.\n\
+\n\
+Exceptions:\n\
+  isc.datasrc.Error Called after commit(), RRset is invalid (see above),\n\
+             internal data source error\n\
+  std.bad_alloc Resource allocation failure\n\
+\n\
+Parameters:\n\
+  rrset      The RRset to be deleted\n\
+\n\
+";
+
+const char* const ZoneUpdater_commit_doc = "\
+commit() -> void\n\
+\n\
+Commit the updates made in the updater to the zone.\n\
+\n\
+This method completes the \"transaction\" started at the creation of\n\
+the updater. After successful completion of this method, the updates\n\
+will be visible outside the scope of the updater. The actual internal\n\
+behavior will defer for different derived classes. For a derived class\n\
+with a general purpose database as a backend, for example, this method\n\
+would perform a \"commit\" statement for the database.\n\
+\n\
+This operation can only be performed at most once. A duplicate call\n\
+must result in a isc.datasrc.Error exception.\n\
+\n\
+Exceptions:\n\
+  isc.datasrc.Error Duplicate call of the method, internal data source\n\
+             error, or wrapper error\n\\n\
+\n\
+";
+} // unnamed namespace
diff --git a/src/lib/python/isc/datasrc/updater_python.cc b/src/lib/python/isc/datasrc/updater_python.cc
new file mode 100644
index 0000000..a9dc581
--- /dev/null
+++ b/src/lib/python/isc/datasrc/updater_python.cc
@@ -0,0 +1,318 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// Enable this if you use s# variants with PyArg_ParseTuple(), see
+// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
+//#define PY_SSIZE_T_CLEAN
+
+// Python.h needs to be placed at the head of the program file, see:
+// http://docs.python.org/py3k/extending/extending.html#a-simple-example
+#include <Python.h>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <datasrc/client.h>
+#include <datasrc/database.h>
+#include <datasrc/data_source.h>
+#include <datasrc/sqlite3_accessor.h>
+#include <datasrc/zone.h>
+
+#include <dns/python/name_python.h>
+#include <dns/python/rrset_python.h>
+#include <dns/python/rrclass_python.h>
+#include <dns/python/rrtype_python.h>
+
+#include "datasrc.h"
+#include "updater_python.h"
+
+#include "updater_inc.cc"
+#include "finder_inc.cc"
+
+using namespace std;
+using namespace isc::util::python;
+using namespace isc::dns::python;
+using namespace isc::datasrc;
+using namespace isc::datasrc::python;
+
+namespace isc_datasrc_internal {
+// See finder_python.cc
+PyObject* ZoneFinder_helper(ZoneFinder* finder, PyObject* args);
+}
+
+namespace {
+// The s_* Class simply covers one instantiation of the object
+class s_ZoneUpdater : public PyObject {
+public:
+    s_ZoneUpdater() : cppobj(ZoneUpdaterPtr()) {};
+    ZoneUpdaterPtr cppobj;
+};
+
+// Shortcut type which would be convenient for adding class variables safely.
+typedef CPPPyObjectContainer<s_ZoneUpdater, ZoneUpdater> ZoneUpdaterContainer;
+
+//
+// We declare the functions here, the definitions are below
+// the type definition of the object, since both can use the other
+//
+
+// General creation and destruction
+int
+ZoneUpdater_init(s_ZoneUpdater* self, PyObject* args) {
+    // can't be called directly
+    PyErr_SetString(PyExc_TypeError,
+                    "ZoneUpdater cannot be constructed directly");
+
+    return (-1);
+}
+
+void
+ZoneUpdater_destroy(s_ZoneUpdater* const self) {
+    // cppobj is a shared ptr, but to make sure things are not destroyed in
+    // the wrong order, we reset it here.
+    self->cppobj.reset();
+    Py_TYPE(self)->tp_free(self);
+}
+
+PyObject*
+ZoneUpdater_addRRset(PyObject* po_self, PyObject* args) {
+    s_ZoneUpdater* const self = static_cast<s_ZoneUpdater*>(po_self);
+    PyObject* rrset_obj;
+    if (PyArg_ParseTuple(args, "O!", &rrset_type, &rrset_obj)) {
+        try {
+            self->cppobj->addRRset(PyRRset_ToRRset(rrset_obj));
+            Py_RETURN_NONE;
+        } catch (const DataSourceError& dse) {
+            PyErr_SetString(getDataSourceException("Error"), dse.what());
+            return (NULL);
+        } catch (const std::exception& exc) {
+            PyErr_SetString(getDataSourceException("Error"), exc.what());
+            return (NULL);
+        }
+    } else {
+        return (NULL);
+    }
+}
+
+PyObject*
+ZoneUpdater_deleteRRset(PyObject* po_self, PyObject* args) {
+    s_ZoneUpdater* const self = static_cast<s_ZoneUpdater*>(po_self);
+    PyObject* rrset_obj;
+    if (PyArg_ParseTuple(args, "O!", &rrset_type, &rrset_obj)) {
+        try {
+            self->cppobj->deleteRRset(PyRRset_ToRRset(rrset_obj));
+            Py_RETURN_NONE;
+        } catch (const DataSourceError& dse) {
+            PyErr_SetString(getDataSourceException("Error"), dse.what());
+            return (NULL);
+        } catch (const std::exception& exc) {
+            PyErr_SetString(getDataSourceException("Error"), exc.what());
+            return (NULL);
+        }
+    } else {
+        return (NULL);
+    }
+}
+
+PyObject*
+ZoneUpdater_commit(PyObject* po_self, PyObject*) {
+    s_ZoneUpdater* const self = static_cast<s_ZoneUpdater*>(po_self);
+    try {
+        self->cppobj->commit();
+        Py_RETURN_NONE;
+    } catch (const DataSourceError& dse) {
+        PyErr_SetString(getDataSourceException("Error"), dse.what());
+        return (NULL);
+    } catch (const std::exception& exc) {
+        PyErr_SetString(getDataSourceException("Error"), exc.what());
+        return (NULL);
+    }
+}
+
+PyObject*
+ZoneUpdater_getClass(PyObject* po_self, PyObject*) {
+    s_ZoneUpdater* self = static_cast<s_ZoneUpdater*>(po_self);
+    try {
+        return (createRRClassObject(self->cppobj->getFinder().getClass()));
+    } catch (const std::exception& exc) {
+        PyErr_SetString(getDataSourceException("Error"), exc.what());
+        return (NULL);
+    } catch (...) {
+        PyErr_SetString(getDataSourceException("Error"),
+                        "Unexpected exception");
+        return (NULL);
+    }
+}
+
+PyObject*
+ZoneUpdater_getOrigin(PyObject* po_self, PyObject*) {
+    s_ZoneUpdater* self = static_cast<s_ZoneUpdater*>(po_self);
+    try {
+        return (createNameObject(self->cppobj->getFinder().getOrigin()));
+    } catch (const std::exception& exc) {
+        PyErr_SetString(getDataSourceException("Error"), exc.what());
+        return (NULL);
+    } catch (...) {
+        PyErr_SetString(getDataSourceException("Error"),
+                        "Unexpected exception");
+        return (NULL);
+    }
+}
+
+PyObject*
+ZoneUpdater_find(PyObject* po_self, PyObject* args) {
+    s_ZoneUpdater* const self = static_cast<s_ZoneUpdater*>(po_self);
+    return (isc_datasrc_internal::ZoneFinder_helper(&self->cppobj->getFinder(),
+                                                    args));
+}
+
+PyObject*
+AZoneUpdater_find(PyObject* po_self, PyObject* args) {
+    s_ZoneUpdater* const self = static_cast<s_ZoneUpdater*>(po_self);
+    PyObject *name;
+    PyObject *rrtype;
+    PyObject *target;
+    int options_int;
+    if (PyArg_ParseTuple(args, "O!O!OI", &name_type, &name,
+                                         &rrtype_type, &rrtype,
+                                         &target, &options_int)) {
+        try {
+            ZoneFinder::FindOptions options =
+                static_cast<ZoneFinder::FindOptions>(options_int);
+            ZoneFinder::FindResult find_result(
+                self->cppobj->getFinder().find(PyName_ToName(name),
+                                   PyRRType_ToRRType(rrtype),
+                                   NULL,
+                                   options
+                                   ));
+            ZoneFinder::Result r = find_result.code;
+            isc::dns::ConstRRsetPtr rrsp = find_result.rrset;
+            if (rrsp) {
+                // Use N instead of O so the refcount isn't increased twice
+                return Py_BuildValue("IN", r, createRRsetObject(*rrsp));
+            } else {
+                return Py_BuildValue("IO", r, Py_None);
+            }
+        } catch (const DataSourceError& dse) {
+            PyErr_SetString(getDataSourceException("Error"), dse.what());
+            return (NULL);
+        } catch (const std::exception& exc) {
+            PyErr_SetString(getDataSourceException("Error"), exc.what());
+            return (NULL);
+        } catch (...) {
+            PyErr_SetString(getDataSourceException("Error"),
+                            "Unexpected exception");
+            return (NULL);
+        }
+    } else {
+        return (NULL);
+    }
+    return Py_BuildValue("I", 1);
+}
+
+
+// This list contains the actual set of functions we have in
+// python. Each entry has
+// 1. Python method name
+// 2. Our static function here
+// 3. Argument type
+// 4. Documentation
+PyMethodDef ZoneUpdater_methods[] = {
+    { "add_rrset", reinterpret_cast<PyCFunction>(ZoneUpdater_addRRset),
+      METH_VARARGS, ZoneUpdater_addRRset_doc },
+    { "delete_rrset", reinterpret_cast<PyCFunction>(ZoneUpdater_deleteRRset),
+      METH_VARARGS, ZoneUpdater_deleteRRset_doc },
+    { "commit", reinterpret_cast<PyCFunction>(ZoneUpdater_commit), METH_NOARGS,
+      ZoneUpdater_commit_doc },
+    // Instead of a getFinder, we implement the finder functionality directly
+    // This is because ZoneFinder is non-copyable, and we should not create
+    // a ZoneFinder object from a reference only (which is what is returned
+    // by getFinder(). Apart from that
+    { "get_origin", reinterpret_cast<PyCFunction>(ZoneUpdater_getOrigin),
+      METH_NOARGS, ZoneFinder_getOrigin_doc },
+    { "get_class", reinterpret_cast<PyCFunction>(ZoneUpdater_getClass),
+      METH_NOARGS, ZoneFinder_getClass_doc },
+    { "find", reinterpret_cast<PyCFunction>(ZoneUpdater_find), METH_VARARGS,
+      ZoneFinder_find_doc },
+    { NULL, NULL, 0, NULL }
+};
+
+} // end of unnamed namespace
+
+namespace isc {
+namespace datasrc {
+namespace python {
+PyTypeObject zoneupdater_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "datasrc.ZoneUpdater",
+    sizeof(s_ZoneUpdater),              // tp_basicsize
+    0,                                  // tp_itemsize
+    reinterpret_cast<destructor>(ZoneUpdater_destroy),// tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash
+    NULL,                               // tp_call
+    NULL,                               // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    ZoneUpdater_doc,
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    NULL,                               // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    ZoneUpdater_methods,                // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    reinterpret_cast<initproc>(ZoneUpdater_init),// tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
+
+PyObject*
+createZoneUpdaterObject(isc::datasrc::ZoneUpdaterPtr source) {
+    s_ZoneUpdater* py_zi = static_cast<s_ZoneUpdater*>(
+        zoneupdater_type.tp_alloc(&zoneupdater_type, 0));
+    if (py_zi != NULL) {
+        py_zi->cppobj = source;
+    }
+    return (py_zi);
+}
+
+} // namespace python
+} // namespace datasrc
+} // namespace isc
+
diff --git a/src/lib/python/isc/datasrc/updater_python.h b/src/lib/python/isc/datasrc/updater_python.h
new file mode 100644
index 0000000..3886aa3
--- /dev/null
+++ b/src/lib/python/isc/datasrc/updater_python.h
@@ -0,0 +1,39 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PYTHON_DATASRC_UPDATER_H
+#define __PYTHON_DATASRC_UPDATER_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace datasrc {
+class DataSourceClient;
+
+namespace python {
+
+
+extern PyTypeObject zoneupdater_type;
+
+PyObject* createZoneUpdaterObject(isc::datasrc::ZoneUpdaterPtr source);
+
+
+} // namespace python
+} // namespace datasrc
+} // namespace isc
+#endif // __PYTHON_DATASRC_UPDATER_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/python/isc/dns/Makefile.am b/src/lib/python/isc/dns/Makefile.am
new file mode 100644
index 0000000..161c2a5
--- /dev/null
+++ b/src/lib/python/isc/dns/Makefile.am
@@ -0,0 +1,7 @@
+python_PYTHON = __init__.py
+
+CLEANDIRS = __pycache__
+
+clean-local:
+	rm -rf $(CLEANDIRS)
+
diff --git a/src/lib/python/isc/log/tests/Makefile.am b/src/lib/python/isc/log/tests/Makefile.am
index a23887c..170eee6 100644
--- a/src/lib/python/isc/log/tests/Makefile.am
+++ b/src/lib/python/isc/log/tests/Makefile.am
@@ -8,7 +8,7 @@ EXTRA_DIST = console.out check_output.sh $(PYTESTS_NOGEN)
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
@@ -16,7 +16,7 @@ endif
 check-local:
 	chmod +x $(abs_builddir)/log_console.py
 	$(LIBRARY_PATH_PLACEHOLDER) \
-	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/python/isc/log \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/python/isc/log \
 	$(abs_srcdir)/check_output.sh $(abs_builddir)/log_console.py $(abs_srcdir)/console.out
 if ENABLE_PYTHON_COVERAGE
 	touch $(abs_top_srcdir)/.coverage
@@ -26,7 +26,7 @@ endif
 	for pytest in $(PYTESTS_NOGEN) ; do \
 	echo Running test: $$pytest ; \
 	$(LIBRARY_PATH_PLACEHOLDER) \
-	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/python/isc/log:$(abs_top_builddir)/src/lib/log/python/.libs \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/python/isc/log:$(abs_top_builddir)/src/lib/log/python/.libs \
 	B10_TEST_PLUGIN_DIR=$(abs_top_srcdir)/src/bin/cfgmgr/plugins \
 	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
 	done ; \
@@ -34,7 +34,7 @@ endif
 	echo Running test: $$pytest ; \
 	chmod +x $(abs_builddir)/$$pytest ; \
 	$(LIBRARY_PATH_PLACEHOLDER) \
-	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/python/isc/log:$(abs_top_builddir)/src/lib/log/python/.libs \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/python/isc/log:$(abs_top_builddir)/src/lib/log/python/.libs \
 	B10_TEST_PLUGIN_DIR=$(abs_top_srcdir)/src/bin/cfgmgr/plugins \
 	$(PYCOVERAGE_RUN) $(abs_builddir)/$$pytest || exit ; \
 	done
diff --git a/src/lib/python/isc/log_messages/Makefile.am b/src/lib/python/isc/log_messages/Makefile.am
new file mode 100644
index 0000000..b9bc4c8
--- /dev/null
+++ b/src/lib/python/isc/log_messages/Makefile.am
@@ -0,0 +1,30 @@
+SUBDIRS = work
+
+EXTRA_DIST = __init__.py
+EXTRA_DIST += bind10_messages.py
+EXTRA_DIST += cmdctl_messages.py
+EXTRA_DIST += stats_messages.py
+EXTRA_DIST += stats_httpd_messages.py
+EXTRA_DIST += xfrin_messages.py
+EXTRA_DIST += xfrout_messages.py
+EXTRA_DIST += zonemgr_messages.py
+EXTRA_DIST += cfgmgr_messages.py
+EXTRA_DIST += config_messages.py
+EXTRA_DIST += notify_out_messages.py
+
+CLEANFILES = __init__.pyc
+CLEANFILES += bind10_messages.pyc
+CLEANFILES += cmdctl_messages.pyc
+CLEANFILES += stats_messages.pyc
+CLEANFILES += stats_httpd_messages.pyc
+CLEANFILES += xfrin_messages.pyc
+CLEANFILES += xfrout_messages.pyc
+CLEANFILES += zonemgr_messages.pyc
+CLEANFILES += cfgmgr_messages.pyc
+CLEANFILES += config_messages.pyc
+CLEANFILES += notify_out_messages.pyc
+
+CLEANDIRS = __pycache__
+
+clean-local:
+	rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/log_messages/README b/src/lib/python/isc/log_messages/README
new file mode 100644
index 0000000..c96f78c
--- /dev/null
+++ b/src/lib/python/isc/log_messages/README
@@ -0,0 +1,68 @@
+This is a placeholder package for logging messages of various modules
+in the form of python scripts.  This package is expected to be installed
+somewhere like <top-install-dir>/python3.x/site-packages/isc/log_messages
+and each message script is expected to be imported as
+"isc.log_messages.some_module_messages".
+
+We also need to allow in-source test code to get access to the message
+scripts in the same manner.  That's why the package is stored in the
+directory that shares the same trailing part as the install directory,
+i.e., isc/log_messages.
+
+Furthermore, we need to support a build mode using a separate build
+tree (such as in the case with 'make distcheck').  In that case if an
+application (via a test script) imports "isc.log_messages.xxx", it
+would try to import the module under the source tree, where the
+generated message script doesn't exist.  So, in the source directory
+(i.e., here) we provide dummy scripts that subsequently import the
+same name of module under the "work" sub-package.  The caller
+application is assumed to have <top_builddir>/src/lib/python/isc/log_messages
+in its module search path (this is done by including
+$(COMMON_PYTHON_PATH) in the PYTHONPATH environment variable),
+which ensures the right directory is chosen.
+
+A python module or program that defines its own log messages needs to
+make sure that the setup described above is implemented.  It's a
+complicated process, but can generally be done by following a common
+pattern:
+
+1. Create the dummy script (see above) for the module and update
+   Makefile.am in this directory accordingly.  See (and use)
+   a helper shell script named gen-forwarder.sh.
+2. Update Makefil.am of the module that defines the log message.  The
+   following are a sample snippet for Makefile.am for a module named
+   "mymodule" (which is supposed to be generated from a file
+   "mymodule_messages.mes").  In many cases it should work simply by
+   replacing 'mymodule' with the actual module name.
+
+====================  begin Makefile.am additions ===================
+nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/mymodule_messages.py
+pylogmessagedir = $(pyexecdir)/isc/log_messages/
+
+CLEANFILES = $(PYTHON_LOGMSGPKG_DIR)/work/mymodule_messages.py
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/mymodule_messages.pyc
+
+EXTRA_DIST = mymodule_messages.mes
+
+$(PYTHON_LOGMSGPKG_DIR)/work/mymodule_messages.py : mymodule_messages.mes
+	$(top_builddir)/src/lib/log/compiler/message \
+	-d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/mymodule_messages.mes
+
+# This rule ensures mymodule_messages.py is (re)generated as a result of
+# 'make'.  If there's no other appropriate target, specify
+# mymodule_messages.py in BUILT_SOURCES.
+mymodule: <other source files> $(PYTHON_LOGMSGPKG_DIR)/work/mymodule_messages.py
+=====================  end Makefile.am additions ====================
+
+Notes:
+- "nodist_" prefix is important.  Without this, 'make distcheck' tries
+  to make _messages.py before actually starting the main build, which
+  would fail because the message compiler isn't built yet.
+- "pylogmessage" is a prefix for python scripts that define log
+  messages and are expected to be installed in the common isc/log_messages
+  directory.   It's intentionally named differently from the common
+  "python" prefix (as in python_PYTHON), because the latter may be
+  used for other scripts in the same Makefile.am file.
+- $(PYTHON_LOGMSGPKG_DIR) should be set to point to this directory (or
+  the corresponding build directory if it's different) by the
+  configure script.
diff --git a/src/lib/python/isc/log_messages/__init__.py b/src/lib/python/isc/log_messages/__init__.py
new file mode 100644
index 0000000..d222b8c
--- /dev/null
+++ b/src/lib/python/isc/log_messages/__init__.py
@@ -0,0 +1,3 @@
+"""
+This is an in-source forwarder package redirecting to work/* scripts.
+"""
diff --git a/src/lib/python/isc/log_messages/bind10_messages.py b/src/lib/python/isc/log_messages/bind10_messages.py
new file mode 100644
index 0000000..68ce94c
--- /dev/null
+++ b/src/lib/python/isc/log_messages/bind10_messages.py
@@ -0,0 +1 @@
+from work.bind10_messages import *
diff --git a/src/lib/python/isc/log_messages/cfgmgr_messages.py b/src/lib/python/isc/log_messages/cfgmgr_messages.py
new file mode 100644
index 0000000..5557100
--- /dev/null
+++ b/src/lib/python/isc/log_messages/cfgmgr_messages.py
@@ -0,0 +1 @@
+from work.cfgmgr_messages import *
diff --git a/src/lib/python/isc/log_messages/cmdctl_messages.py b/src/lib/python/isc/log_messages/cmdctl_messages.py
new file mode 100644
index 0000000..7283d5a
--- /dev/null
+++ b/src/lib/python/isc/log_messages/cmdctl_messages.py
@@ -0,0 +1 @@
+from work.cmdctl_messages import *
diff --git a/src/lib/python/isc/log_messages/config_messages.py b/src/lib/python/isc/log_messages/config_messages.py
new file mode 100644
index 0000000..c557975
--- /dev/null
+++ b/src/lib/python/isc/log_messages/config_messages.py
@@ -0,0 +1 @@
+from work.config_messages import *
diff --git a/src/lib/python/isc/log_messages/gen-forwarder.sh b/src/lib/python/isc/log_messages/gen-forwarder.sh
new file mode 100755
index 0000000..84c2450
--- /dev/null
+++ b/src/lib/python/isc/log_messages/gen-forwarder.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+MODULE_NAME=$1
+if test -z $MODULE_NAME; then
+	echo 'Usage: gen-forwarder.sh module_name'
+	exit 1
+fi
+
+echo "from work.${MODULE_NAME}_messages import *" > ${MODULE_NAME}_messages.py
+echo "Forwarder python script is generated.  Make sure to perform:"
+echo "git add ${MODULE_NAME}_messages.py"
+echo "and add the following to Makefile.am:"
+echo "EXTRA_DIST += ${MODULE_NAME}_messages.py"
+echo "CLEANFILES += ${MODULE_NAME}_messages.pyc"
diff --git a/src/lib/python/isc/log_messages/notify_out_messages.py b/src/lib/python/isc/log_messages/notify_out_messages.py
new file mode 100644
index 0000000..6aa37ea
--- /dev/null
+++ b/src/lib/python/isc/log_messages/notify_out_messages.py
@@ -0,0 +1 @@
+from work.notify_out_messages import *
diff --git a/src/lib/python/isc/log_messages/stats_httpd_messages.py b/src/lib/python/isc/log_messages/stats_httpd_messages.py
new file mode 100644
index 0000000..7782c34
--- /dev/null
+++ b/src/lib/python/isc/log_messages/stats_httpd_messages.py
@@ -0,0 +1 @@
+from work.stats_httpd_messages import *
diff --git a/src/lib/python/isc/log_messages/stats_messages.py b/src/lib/python/isc/log_messages/stats_messages.py
new file mode 100644
index 0000000..1324cfc
--- /dev/null
+++ b/src/lib/python/isc/log_messages/stats_messages.py
@@ -0,0 +1 @@
+from work.stats_messages import *
diff --git a/src/lib/python/isc/log_messages/work/Makefile.am b/src/lib/python/isc/log_messages/work/Makefile.am
new file mode 100644
index 0000000..9bc5e0f
--- /dev/null
+++ b/src/lib/python/isc/log_messages/work/Makefile.am
@@ -0,0 +1,12 @@
+# .py is generated in the builddir by the configure script so that test
+# scripts can refer to it when a separate builddir is used.
+
+python_PYTHON = __init__.py
+
+pythondir = $(pyexecdir)/isc/log_messages/
+
+CLEANFILES = __init__.pyc
+CLEANDIRS = __pycache__
+
+clean-local:
+	rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/log_messages/work/__init__.py.in b/src/lib/python/isc/log_messages/work/__init__.py.in
new file mode 100644
index 0000000..991f10a
--- /dev/null
+++ b/src/lib/python/isc/log_messages/work/__init__.py.in
@@ -0,0 +1,3 @@
+"""
+This package is a placeholder for python scripts of log messages.
+"""
diff --git a/src/lib/python/isc/log_messages/xfrin_messages.py b/src/lib/python/isc/log_messages/xfrin_messages.py
new file mode 100644
index 0000000..b412519
--- /dev/null
+++ b/src/lib/python/isc/log_messages/xfrin_messages.py
@@ -0,0 +1 @@
+from work.xfrin_messages import *
diff --git a/src/lib/python/isc/log_messages/xfrout_messages.py b/src/lib/python/isc/log_messages/xfrout_messages.py
new file mode 100644
index 0000000..2093d5c
--- /dev/null
+++ b/src/lib/python/isc/log_messages/xfrout_messages.py
@@ -0,0 +1 @@
+from work.xfrout_messages import *
diff --git a/src/lib/python/isc/log_messages/zonemgr_messages.py b/src/lib/python/isc/log_messages/zonemgr_messages.py
new file mode 100644
index 0000000..b3afe9c
--- /dev/null
+++ b/src/lib/python/isc/log_messages/zonemgr_messages.py
@@ -0,0 +1 @@
+from work.zonemgr_messages import *
diff --git a/src/lib/python/isc/net/tests/Makefile.am b/src/lib/python/isc/net/tests/Makefile.am
index 3a04f17..dd94946 100644
--- a/src/lib/python/isc/net/tests/Makefile.am
+++ b/src/lib/python/isc/net/tests/Makefile.am
@@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS)
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
@@ -19,6 +19,6 @@ endif
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	$(LIBRARY_PATH_PLACEHOLDER) \
-	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/dns/python/.libs \
 	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
 	done
diff --git a/src/lib/python/isc/notify/Makefile.am b/src/lib/python/isc/notify/Makefile.am
index a23a1ff..c247ab8 100644
--- a/src/lib/python/isc/notify/Makefile.am
+++ b/src/lib/python/isc/notify/Makefile.am
@@ -1,21 +1,22 @@
 SUBDIRS = . tests
 
 python_PYTHON = __init__.py notify_out.py
-pyexec_DATA = $(top_builddir)/src/lib/python/notify_out_messages.py
-
 pythondir = $(pyexecdir)/isc/notify
 
-$(top_builddir)/src/lib/python/notify_out_messages.py: notify_out_messages.mes
-	$(top_builddir)/src/lib/log/compiler/message \
-		-p -d $(top_builddir)/src/lib/python \
-		$(top_srcdir)/src/lib/python/isc/notify/notify_out_messages.mes
+BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/work/notify_out_messages.py
+nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/notify_out_messages.py
+pylogmessagedir = $(pyexecdir)/isc/log_messages/
 
 EXTRA_DIST = notify_out_messages.mes
 
-CLEANFILES =  $(top_builddir)/src/lib/python/notify_out_messages.pyc
-CLEANFILES += $(top_builddir)/src/lib/python/notify_out_messages.py
+CLEANFILES = $(PYTHON_LOGMSGPKG_DIR)/work/notify_out_messages.py
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/notify_out_messages.pyc
 
 CLEANDIRS = __pycache__
 
+$(PYTHON_LOGMSGPKG_DIR)/work/notify_out_messages.py : notify_out_messages.mes
+	$(top_builddir)/src/lib/log/compiler/message \
+	-d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/notify_out_messages.mes
+
 clean-local:
 	rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/notify/notify_out.py b/src/lib/python/isc/notify/notify_out.py
index f1e02ca..6b91c87 100644
--- a/src/lib/python/isc/notify/notify_out.py
+++ b/src/lib/python/isc/notify/notify_out.py
@@ -23,7 +23,7 @@ import errno
 from isc.datasrc import sqlite3_ds
 from isc.net import addr
 import isc
-from notify_out_messages import *
+from isc.log_messages.notify_out_messages import *
 
 logger = isc.log.Logger("notify_out")
 
diff --git a/src/lib/python/isc/notify/tests/Makefile.am b/src/lib/python/isc/notify/tests/Makefile.am
index 1427d93..00c2eee 100644
--- a/src/lib/python/isc/notify/tests/Makefile.am
+++ b/src/lib/python/isc/notify/tests/Makefile.am
@@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS)
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
@@ -18,7 +18,7 @@ if ENABLE_PYTHON_COVERAGE
 endif
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
-	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/dns/python/.libs \
 	$(LIBRARY_PATH_PLACEHOLDER) \
 	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
 	done
diff --git a/src/lib/python/isc/util/tests/Makefile.am b/src/lib/python/isc/util/tests/Makefile.am
index c3d35c2..3b882b4 100644
--- a/src/lib/python/isc/util/tests/Makefile.am
+++ b/src/lib/python/isc/util/tests/Makefile.am
@@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS)
 # required by loadable python modules.
 LIBRARY_PATH_PLACEHOLDER =
 if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
@@ -19,6 +19,6 @@ endif
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	$(LIBRARY_PATH_PLACEHOLDER) \
-	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/dns/python/.libs \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/dns/python/.libs \
 	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
 	done
diff --git a/src/lib/util/python/pycppwrapper_util.h b/src/lib/util/python/pycppwrapper_util.h
index 3f396e2..462e715 100644
--- a/src/lib/util/python/pycppwrapper_util.h
+++ b/src/lib/util/python/pycppwrapper_util.h
@@ -293,7 +293,7 @@ protected:
 /// \c PyObject_New() to the caller.
 template <typename PYSTRUCT, typename CPPCLASS>
 struct CPPPyObjectContainer : public PyObjectContainer {
-    CPPPyObjectContainer(PYSTRUCT* obj) : PyObjectContainer(obj) {}
+    explicit CPPPyObjectContainer(PYSTRUCT* obj) : PyObjectContainer(obj) {}
 
     // This method associates a C++ object with the corresponding python
     // object enclosed in this class.
diff --git a/src/lib/util/python/wrapper_template.cc b/src/lib/util/python/wrapper_template.cc
index a703731..426ced5 100644
--- a/src/lib/util/python/wrapper_template.cc
+++ b/src/lib/util/python/wrapper_template.cc
@@ -299,8 +299,8 @@ initModulePart_ at CPPCLASS@(PyObject* mod) {
 
 PyObject*
 create at CPPCLASS@Object(const @CPPCLASS@& source) {
-    @CPPCLASS at Container container =
-        PyObject_New(s_ at CPPCLASS@, &@cppclass at _type);
+    @CPPCLASS at Container container(PyObject_New(s_ at CPPCLASS@,
+                                               &@cppclass at _type));
     container.set(new @CPPCLASS@(source));
     return (container.release());
 }
diff --git a/src/lib/util/python/wrapper_template.h b/src/lib/util/python/wrapper_template.h
index d68a658..be701e1 100644
--- a/src/lib/util/python/wrapper_template.h
+++ b/src/lib/util/python/wrapper_template.h
@@ -37,15 +37,15 @@ bool initModulePart_ at CPPCLASS@(PyObject* mod);
 // Note: this utility function works only when @CPPCLASS@ is a copy
 // constructable.
 // And, it would only be useful when python binding needs to create this
-// object frequently.  Otherwise, it would (or should) probably better to
+// object frequently.  Otherwise, it would (or should) probably be better to
 // remove the declaration and definition of this function.
 //
-/// This is A simple shortcut to create a python @CPPCLASS@ object (in the
+/// This is a simple shortcut to create a python @CPPCLASS@ object (in the
 /// form of a pointer to PyObject) with minimal exception safety.
 /// On success, it returns a valid pointer to PyObject with a reference
 /// counter of 1; if something goes wrong it throws an exception (it never
 /// returns a NULL pointer).
-/// This function is expected to be called with in a try block
+/// This function is expected to be called within a try block
 /// followed by necessary setup for python exception.
 PyObject* create at CPPCLASS@Object(const @CPPCLASS@& source);
 




More information about the bind10-changes mailing list