From a115eec9ff3ebbc0d897f26ffcccbc3c90bb7194 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Mon, 14 Oct 2013 00:56:53 -0300 Subject: [PATCH 01/56] workaround for sphinx assertion error We were hitting sphinx bug documented at https://bitbucket.org/birkenfeld/sphinx/issue/1160 This workaround makes those assertion error dissapear and sphinx to finish happily. --- docs/index.rst | 2 +- gnupg/_meta.py | 6 +++--- gnupg/_trust.py | 8 ++++---- gnupg/gnupg.py | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index bafe40e..c266353 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -21,7 +21,7 @@ Contents: Source, license, & bug reports ============================== The source code which was used to generate this documentation is accessible by -clicking the little [source]_ links next to the docs. Current source code can +clicking the little `source` links next to the docs. Current source code can be found in this github repository_. The **master** branch always reflects the latest release, all releases are tagged with signed, annotated git tags, and the **develop** branch represents the state of the next release. diff --git a/gnupg/_meta.py b/gnupg/_meta.py index 57a0dfb..cce747e 100644 --- a/gnupg/_meta.py +++ b/gnupg/_meta.py @@ -7,12 +7,12 @@ # © 2008-2012 Vinay Sajip # © 2005 Steve Traugott # © 2004 A.M. Kuchling -# +# # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) # any later version. -# +# # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the included LICENSE file for details. @@ -604,7 +604,7 @@ class GPGBase(object): :param str keyids: A space-delimited string containing the keyids to request. :param str keyserver: The keyserver to request the ``keyids`` from; - defaults to :property:`gnupg.GPG.keyserver`. + defaults to `gnupg.GPG.keyserver`. """ if not keyserver: keyserver = self.keyserver diff --git a/gnupg/_trust.py b/gnupg/_trust.py index 30f7439..1f5b266 100644 --- a/gnupg/_trust.py +++ b/gnupg/_trust.py @@ -7,12 +7,12 @@ # © 2008-2012 Vinay Sajip # © 2005 Steve Traugott # © 2004 A.M. Kuchling -# +# # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) # any later version. -# +# # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the included LICENSE file for details. @@ -78,11 +78,11 @@ def import_ownertrust(self, trustdb=None): def fix_trustdb(cls, trustdb=None): """Attempt to repair a broken trustdb.gpg file. - GnuPG>=2.0.x has this magical-seeming flag: '--fix-trustdb'. You'd think + GnuPG>=2.0.x has this magical-seeming flag: `--fix-trustdb`. You'd think it would fix the the trustdb. Hah! It doesn't. Here's what it does instead: - (python-gnupg)∃!isisⒶwintermute:(testing/digest-algo *$=)~/code/python-gnupg ∴ gpg2 --fix-trustdb + (python-gnupg)∃!isisⒶwintermute:(testing/digest-algo)~/code/python-gnupg ∴ gpg2 --fix-trustdb gpg: You may try to re-create the trustdb using the commands: gpg: cd ~/.gnupg gpg: gpg2 --export-ownertrust > otrust.tmp diff --git a/gnupg/gnupg.py b/gnupg/gnupg.py index 0605f68..c907a32 100644 --- a/gnupg/gnupg.py +++ b/gnupg/gnupg.py @@ -7,12 +7,12 @@ # © 2008-2012 Vinay Sajip # © 2005 Steve Traugott # © 2004 A.M. Kuchling -# +# # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) # any later version. -# +# # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the included LICENSE file for details. @@ -373,7 +373,7 @@ class GPG(GPGBase): :param str keyids: Each ``keyids`` argument should be a string containing a keyid to request. :param str keyserver: The keyserver to request the ``keyids`` from; - defaults to :property:`gnupg.GPG.keyserver`. + defaults to `gnupg.GPG.keyserver`. """ if keyids: keys = ' '.join([key for key in keyids]) From 15caea75b38412d602c933c6d4a853321911d5a3 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Mon, 14 Oct 2013 01:17:43 -0300 Subject: [PATCH 02/56] fix sphinx errors&warnings; some pep8 --- gnupg/__init__.py | 26 ++++++------ gnupg/_meta.py | 102 +++++++++++++++++++++++++++------------------- gnupg/gnupg.py | 29 ++++++------- 3 files changed, 89 insertions(+), 68 deletions(-) diff --git a/gnupg/__init__.py b/gnupg/__init__.py index 982ffbf..5c1430c 100644 --- a/gnupg/__init__.py +++ b/gnupg/__init__.py @@ -8,31 +8,31 @@ # © 2008-2012 Vinay Sajip # © 2005 Steve Traugott # © 2004 A.M. Kuchling -# +# # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) # any later version. -# +# # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the included LICENSE file for details. from __future__ import absolute_import -from . import gnupg -from . import copyleft -from . import _ansistrm -from . import _logger -from . import _meta -from . import _parsers -from . import _util -from .gnupg import GPG +from . import gnupg +from . import copyleft +from . import _ansistrm +from . import _logger +from . import _meta +from . import _parsers +from . import _util +from .gnupg import GPG from ._version import get_versions -__version__ = get_versions()['version'] -__authors__ = copyleft.authors -__license__ = copyleft.full_text +__version__ = get_versions()['version'] +__authors__ = copyleft.authors +__license__ = copyleft.full_text __copyleft__ = copyleft.copyright ## do not set __package__ = "gnupg", else we will end up with diff --git a/gnupg/_meta.py b/gnupg/_meta.py index cce747e..bebf490 100644 --- a/gnupg/_meta.py +++ b/gnupg/_meta.py @@ -87,7 +87,8 @@ class GPGMeta(type): class GPGBase(object): - """Base class for property storage and to control process initialisation.""" + """Base class for property storage and to control process + initialisation.""" __metaclass__ = GPGMeta _decode_errors = 'strict' @@ -631,9 +632,10 @@ class GPGBase(object): :param bool detach: If True, create a detached signature. :param bool binary: If True, do not ascii armour the output. :param str digest_algo: The hash digest to use. Again, to see which - hashes your GnuPG is capable of using, do: - ``$ gpg --with-colons --list-config digestname``. - The default, if unspecified, is ``'SHA512'``. + hashes your GnuPG is capable of using, do: + ``$ gpg --with-colons --list-config + digestname``. The default, if unspecified, is + ``'SHA512'``. """ log.debug("_sign_file():") if binary: @@ -685,42 +687,55 @@ class GPGBase(object): :param str data: The file or bytestream to encrypt. - :param str recipients: The recipients to encrypt to. Recipients must - be specified keyID/fingerprint. Care should be taken in Python2.x - to make sure that the given fingerprint is in fact a string and - not a unicode object. + :param recipients: The recipients to encrypt to. Recipients must be + specified keyID/fingerprint. Care should be taken + in Python2.x to make sure that the given fingerprint + is in fact a string and not a unicode object. + :type recipients: str - :param str default_key: The keyID/fingerprint of the key to use for - signing. If given, ``data`` will be encrypted and signed. + :param default_key: The keyID/fingerprint of the key to use for + signing. If given, ``data`` will be encrypted + and signed. + :type default_key: str - :param str passphrase: If given, and ``default_key`` is also given, - use this passphrase to unlock the secret portion of the - ``default_key`` to sign the encrypted ``data``. Otherwise, if - ``default_key`` is not given, but ``symmetric=True``, then use - this passphrase as the passphrase for symmetric - encryption. Signing and symmetric encryption should *not* be - combined when sending the ``data`` to other recipients, else the - passphrase to the secret key would be shared with them. + :param passphrase: If given, and ``default_key`` is also given, + use this passphrase to unlock the secret portion of + the ``default_key`` to sign the encrypted ``data``. + Otherwise, if ``default_key`` is not given, but + ``symmetric=True``, then use this passphrase as the + passphrase for symmetric encryption. Signing and + symmetric encryption should *not* be combined when + sending the ``data`` to other recipients, else the + passphrase to the secret key would be shared with + them. + :type passphrase: str - :param bool armor: If True, ascii armor the output; otherwise, the - output will be in binary format. (Default: True) + :param armor: If True, ascii armor the output; otherwise, the output + will be in binary format. (Default: True) + :type armor: bool - :param bool encrypt: If True, encrypt the ``data`` using the - ``recipients`` public keys. (Default: True) + :param encrypt: If True, encrypt the ``data`` using the ``recipients`` + public keys. (Default: True) + :type encrypt: bool - :param bool symmetric: If True, encrypt the ``data`` to ``recipients`` - using a symmetric key. See the ``passphrase`` parameter. Symmetric - encryption and public key encryption can be used simultaneously, - and will result in a ciphertext which is decryptable with either - the symmetric ``passphrase`` or one of the corresponding private - keys. + :param symmetric: If True, encrypt the ``data`` to ``recipients`` + using a symmetric key. See the ``passphrase`` + parameter. Symmetric encryption and public key + encryption can be used simultaneously, and will + result in a ciphertext which is decryptable with + either the symmetric ``passphrase`` or one of the + corresponding private keys. + :type symmetric: bool - :param bool always_trust: If True, ignore trust warnings on recipient - keys. If False, display trust warnings. (default: True) + :param always_trust: If True, ignore trust warnings on recipient + keys. If False, display trust warnings. + (default: True) + :type always_trust: bool - :param str output: The output file to write to. If not specified, the - encrypted output is returned, and thus should be stored as an - object in Python. For example: + :param output: The output file to write to. If not specified, the + encrypted output is returned, and thus should be + stored as an object in Python. For example: + :type output: str >>> import shutil >>> import gnupg @@ -742,16 +757,21 @@ class GPGBase(object): 'The crow flies at midnight.' :param str cipher_algo: The cipher algorithm to use. To see available - algorithms with your version of GnuPG, do: - ``$ gpg --with-colons --list-config ciphername``. - The default ``cipher_algo``, if unspecified, is ``'AES256'``. + algorithms with your version of GnuPG, do: + ``$ gpg --with-colons --list-config + ciphername``. + The default ``cipher_algo``, if unspecified, + is ``'AES256'``. + :type cipher_algo: str - :param str digest_algo: The hash digest to use. Again, to see which - hashes your GnuPG is capable of using, do: - ``$ gpg --with-colons --list-config digestname``. - The default, if unspecified, is ``'SHA512'``. + :param digest_algo: The hash digest to use. Again, to see which + hashes your GnuPG is capable of using, do: + ``$ gpg --with-colons --list-config digestname``. + The default, if unspecified, is ``'SHA512'``. + :type digest_algo: str - :param str compress_algo: The compression algorithm to use. Can be one + :param compress_algo: The compression algorithm to use. Can be one + :type compress_algo: str of ``'ZLIB'``, ``'BZIP2'``, ``'ZIP'``, or ``'Uncompressed'``. """ args = [] diff --git a/gnupg/gnupg.py b/gnupg/gnupg.py index c907a32..1b350e1 100644 --- a/gnupg/gnupg.py +++ b/gnupg/gnupg.py @@ -389,22 +389,23 @@ class GPG(GPGBase): be deleted from :attr:`GPG.secring`. :type fingerprints: str or list or tuple - :param fingerprints: A string, or a list/tuple of strings, - representing the fingerprint(s) for the key(s) to delete. + representing the fingerprint(s) for the key(s) + to delete. :param bool secret: If True, delete the corresponding secret key(s) - also. (default: False) + also. (default: False) :param bool subkeys: If True, delete the secret subkey first, then the - public key. (default: False) Same as: - ``$ gpg --delete-secret-and-public-key 0x12345678`` + public key. (default: False) Same as: + ``$ gpg --delete-secret-and-public-key + 0x12345678`` """ - which='keys' + which = 'keys' if secret: - which='secret-keys' + which = 'secret-keys' if subkeys: - which='secret-and-public-keys' + which = 'secret-and-public-keys' if _is_list_or_tuple(fingerprints): fingerprints = ' '.join(fingerprints) @@ -425,11 +426,11 @@ class GPG(GPGBase): :param bool secret: If True, export only the secret key. :param bool subkeys: If True, export the secret subkeys. """ - which='' + which = '' if subkeys: - which='-secret-subkeys' + which = '-secret-subkeys' elif secret: - which='-secret-keys' + which = '-secret-keys' if _is_list_or_tuple(keyids): keyids = ' '.join(['%s' % k for k in keyids]) @@ -441,7 +442,7 @@ class GPG(GPGBase): ## gpg --export produces no status-fd output; stdout will be empty in ## case of failure #stdout, stderr = p.communicate() - result = self._result_map['delete'](self) # any result will do + result = self._result_map['delete'](self) # any result will do self._collect_output(p, result, stdin=p.stdin) log.debug('Exported:%s%r' % (os.linesep, result.data)) return result.data.decode(self._encoding, self._decode_errors) @@ -467,9 +468,9 @@ class GPG(GPGBase): >>> assert print2 in pubkeys.fingerprints """ - which='public-keys' + which = 'public-keys' if secret: - which='secret-keys' + which = 'secret-keys' args = "--list-%s --fixed-list-mode --fingerprint " % (which,) args += "--with-colons --list-options no-show-photos" args = [args] From 91febabd3250effeee8013ffa91d9d471b01b842 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Mon, 14 Oct 2013 01:19:16 -0300 Subject: [PATCH 03/56] add docs/_build to gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index d322edd..a8bfc2b 100644 --- a/.gitignore +++ b/.gitignore @@ -83,3 +83,6 @@ gpg # setuptools/distribute files: PKG-INFO MANIFEST + +# sphinx default build +docs/_build From e5351cfe957cfacab60dded18bc825aa5a4a685b Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Mon, 14 Oct 2013 02:49:30 -0300 Subject: [PATCH 04/56] remove docs/_static folder. It was giving troubles with debian dh_sphinxdoc, due to an old, unrecognized version of jquery being there that could not be symlinked to the js-sphinxdoc version. --- .gitignore | 1 + docs/_static/agogo.css | 327 -------------------------------- docs/_static/doctools.js | 232 ----------------------- docs/_static/haiku.css | 386 -------------------------------------- docs/_static/jquery.js | 32 ---- docs/_static/pygments.css | 69 ------- 6 files changed, 1 insertion(+), 1046 deletions(-) delete mode 100644 docs/_static/agogo.css delete mode 100644 docs/_static/doctools.js delete mode 100644 docs/_static/haiku.css delete mode 100644 docs/_static/jquery.js delete mode 100644 docs/_static/pygments.css diff --git a/.gitignore b/.gitignore index a8bfc2b..d39197d 100644 --- a/.gitignore +++ b/.gitignore @@ -86,3 +86,4 @@ MANIFEST # sphinx default build docs/_build +docs/_static diff --git a/docs/_static/agogo.css b/docs/_static/agogo.css deleted file mode 100644 index 9d6a4af..0000000 --- a/docs/_static/agogo.css +++ /dev/null @@ -1,327 +0,0 @@ -* { - margin: 0px; - padding: 0px; -} - -body { - font-family: "Verdana", Arial, sans-serif; - line-height: 1.4em; - font-size: 14px; - color: black; - background-color: #eeeeec; -} - - -/* Page layout */ - -div.header, div.content, div.footer { - width: 70em; - margin-left: auto; - margin-right: auto; -} - -div.header-wrapper { - background: url(bgtop.png) top left repeat-x; - border-bottom: 3px solid #2e3436; -} - - -/* Default body styles */ -a { - text-decoration: none; - color: #ce5c00; -} - -.clearer { - clear: both; -} - -.left { - float: left; -} - -.right { - float: right; -} - -h1, h2, h3, h4 { - font-family: "Georgia", "Times New Roman", serif; - font-weight: normal; - color: #3465a4; - margin-bottom: .8em; -} - -h1 { - color: #204a87; -} - -h2 { - padding-bottom: .5em; - border-bottom: 1px solid #3465a4; -} - -a.headerlink { - visibility: hidden; - color: #dddddd; - padding-left: .3em; -} - -h1:hover > a.headerlink, -h2:hover > a.headerlink, -h3:hover > a.headerlink, -h4:hover > a.headerlink, -h5:hover > a.headerlink, -h6:hover > a.headerlink, -dt:hover > a.headerlink { - visibility: visible; -} - - - -/* Header */ - -div.header { - padding-top: 10px; - padding-bottom: 10px; -} - -div.header h1 { - font-family: "Georgia", "Times New Roman", serif; - font-weight: normal; - font-size: 160%; - letter-spacing: .08em; -} - -div.header h1 a { - color: white; -} - -div.header div.rel { - margin-top: 1em; -} - -div.header div.rel a { - color: #fcaf3e; - letter-spacing: .1em; - text-transform: uppercase; -} - - -/* Content */ -div.content-wrapper { - background-color: white; - padding-top: 20px; - padding-bottom: 20px; -} - -div.document { - width: 50em; - float: left; -} - -div.body { - padding-right: 2em; - text-align: justify; -} - -div.document ul { - margin-left: 1.2em; - list-style-type: square; -} - -div.document dd { - margin-left: 1.2em; - margin-top: .4em; - margin-bottom: 1em; -} - -div.document .section { - margin-top: 1.7em; -} -div.document .section:first-child { - margin-top: 0px; -} - -div.document div.highlight { - padding: 3px; - background-color: #eeeeec; - border-top: 2px solid #dddddd; - border-bottom: 2px solid #dddddd; - margin-top: .8em; - margin-bottom: .8em; -} - -div.document h2 { - margin-top: .7em; -} - -div.document p { - margin-bottom: .5em; -} - -div.document li.toctree-l1 { - margin-bottom: 1em; -} - -div.document .descname { - font-weight: bold; -} - -div.document .docutils.literal { - background-color: #eeeeec; - padding: 1px; -} - -div.document .docutils.xref.literal { - background-color: transparent; - padding: 0px; -} - - -/* Sidebar */ - -div.sidebar { - width: 20em; - float: right; - font-size: .9em; -} - -div.sidebar h3 { - color: #2e3436; - text-transform: uppercase; - font-size: 130%; - letter-spacing: .1em; -} - -div.sidebar ul { - list-style-type: none; -} - -div.sidebar li.toctree-l1 a { - display: block; - padding: 1px; - border: 1px solid #dddddd; - background-color: #eeeeec; - margin-bottom: .4em; - padding-left: 3px; - color: #2e3436; -} - -div.sidebar li.toctree-l2 a { - background-color: transparent; - border: none; - border-bottom: 1px solid #dddddd; -} - -div.sidebar li.toctree-l2:last-child a { - border-bottom: none; -} - -div.sidebar li.toctree-l1.current a { - border-right: 5px solid #fcaf3e; -} - -div.sidebar li.toctree-l1.current li.toctree-l2 a { - border-right: none; -} - - -/* Footer */ - -div.footer-wrapper { - background: url(bgfooter.png) top left repeat-x; - border-top: 4px solid #babdb6; - padding-top: 10px; - padding-bottom: 10px; - min-height: 80px; -} - -div.footer, div.footer a { - color: #888a85; -} - -div.footer .right { - text-align: right; -} - -div.footer .left { - text-transform: uppercase; -} - - -/* Styles copied form basic theme */ - -/* -- search page ----------------------------------------------------------- */ - -ul.search { - margin: 10px 0 0 20px; - padding: 0; -} - -ul.search li { - padding: 5px 0 5px 20px; - background-image: url(file.png); - background-repeat: no-repeat; - background-position: 0 7px; -} - -ul.search li a { - font-weight: bold; -} - -ul.search li div.context { - color: #888; - margin: 2px 0 0 30px; - text-align: left; -} - -ul.keywordmatches li.goodmatch a { - font-weight: bold; -} - -/* -- index page ------------------------------------------------------------ */ - -table.contentstable { - width: 90%; -} - -table.contentstable p.biglink { - line-height: 150%; -} - -a.biglink { - font-size: 1.3em; -} - -span.linkdescr { - font-style: italic; - padding-top: 5px; - font-size: 90%; -} - -/* -- general index --------------------------------------------------------- */ - -table.indextable td { - text-align: left; - vertical-align: top; -} - -table.indextable dl, table.indextable dd { - margin-top: 0; - margin-bottom: 0; -} - -table.indextable tr.pcap { - height: 10px; -} - -table.indextable tr.cap { - margin-top: 10px; - background-color: #f2f2f2; -} - -img.toggler { - margin-right: 3px; - margin-top: 3px; - cursor: pointer; -} diff --git a/docs/_static/doctools.js b/docs/_static/doctools.js deleted file mode 100644 index 9447678..0000000 --- a/docs/_static/doctools.js +++ /dev/null @@ -1,232 +0,0 @@ -/// XXX: make it cross browser - -/** - * make the code below compatible with browsers without - * an installed firebug like debugger - */ -if (!window.console || !console.firebug) { - var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", - "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"]; - window.console = {}; - for (var i = 0; i < names.length; ++i) - window.console[names[i]] = function() {} -} - -/** - * small helper function to urldecode strings - */ -jQuery.urldecode = function(x) { - return decodeURIComponent(x).replace(/\+/g, ' '); -} - -/** - * small helper function to urlencode strings - */ -jQuery.urlencode = encodeURIComponent; - -/** - * This function returns the parsed url parameters of the - * current request. Multiple values per key are supported, - * it will always return arrays of strings for the value parts. - */ -jQuery.getQueryParameters = function(s) { - if (typeof s == 'undefined') - s = document.location.search; - var parts = s.substr(s.indexOf('?') + 1).split('&'); - var result = {}; - for (var i = 0; i < parts.length; i++) { - var tmp = parts[i].split('=', 2); - var key = jQuery.urldecode(tmp[0]); - var value = jQuery.urldecode(tmp[1]); - if (key in result) - result[key].push(value); - else - result[key] = [value]; - } - return result; -} - -/** - * small function to check if an array contains - * a given item. - */ -jQuery.contains = function(arr, item) { - for (var i = 0; i < arr.length; i++) { - if (arr[i] == item) - return true; - } - return false; -} - -/** - * highlight a given string on a jquery object by wrapping it in - * span elements with the given class name. - */ -jQuery.fn.highlightText = function(text, className) { - function highlight(node) { - if (node.nodeType == 3) { - var val = node.nodeValue; - var pos = val.toLowerCase().indexOf(text); - if (pos >= 0 && !jQuery.className.has(node.parentNode, className)) { - var span = document.createElement("span"); - span.className = className; - span.appendChild(document.createTextNode(val.substr(pos, text.length))); - node.parentNode.insertBefore(span, node.parentNode.insertBefore( - document.createTextNode(val.substr(pos + text.length)), - node.nextSibling)); - node.nodeValue = val.substr(0, pos); - } - } - else if (!jQuery(node).is("button, select, textarea")) { - jQuery.each(node.childNodes, function() { - highlight(this) - }); - } - } - return this.each(function() { - highlight(this); - }); -} - -/** - * Small JavaScript module for the documentation. - */ -var Documentation = { - - init : function() { - this.fixFirefoxAnchorBug(); - this.highlightSearchWords(); - this.initModIndex(); - }, - - /** - * i18n support - */ - TRANSLATIONS : {}, - PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; }, - LOCALE : 'unknown', - - // gettext and ngettext don't access this so that the functions - // can savely bound to a different name (_ = Documentation.gettext) - gettext : function(string) { - var translated = Documentation.TRANSLATIONS[string]; - if (typeof translated == 'undefined') - return string; - return (typeof translated == 'string') ? translated : translated[0]; - }, - - ngettext : function(singular, plural, n) { - var translated = Documentation.TRANSLATIONS[singular]; - if (typeof translated == 'undefined') - return (n == 1) ? singular : plural; - return translated[Documentation.PLURALEXPR(n)]; - }, - - addTranslations : function(catalog) { - for (var key in catalog.messages) - this.TRANSLATIONS[key] = catalog.messages[key]; - this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); - this.LOCALE = catalog.locale; - }, - - /** - * add context elements like header anchor links - */ - addContextElements : function() { - $('div[id] > :header:first').each(function() { - $('\u00B6'). - attr('href', '#' + this.id). - attr('title', _('Permalink to this headline')). - appendTo(this); - }); - $('dt[id]').each(function() { - $('\u00B6'). - attr('href', '#' + this.id). - attr('title', _('Permalink to this definition')). - appendTo(this); - }); - }, - - /** - * workaround a firefox stupidity - */ - fixFirefoxAnchorBug : function() { - if (document.location.hash && $.browser.mozilla) - window.setTimeout(function() { - document.location.href += ''; - }, 10); - }, - - /** - * highlight the search words provided in the url in the text - */ - highlightSearchWords : function() { - var params = $.getQueryParameters(); - var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; - if (terms.length) { - var body = $('div.body'); - window.setTimeout(function() { - $.each(terms, function() { - body.highlightText(this.toLowerCase(), 'highlight'); - }); - }, 10); - $('') - .appendTo($('.sidebar .this-page-menu')); - } - }, - - /** - * init the modindex toggle buttons - */ - initModIndex : function() { - var togglers = $('img.toggler').click(function() { - var src = $(this).attr('src'); - var idnum = $(this).attr('id').substr(7); - console.log($('tr.cg-' + idnum).toggle()); - if (src.substr(-9) == 'minus.png') - $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); - else - $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); - }).css('display', ''); - if (DOCUMENTATION_OPTIONS.COLLAPSE_MODINDEX) { - togglers.click(); - } - }, - - /** - * helper function to hide the search marks again - */ - hideSearchWords : function() { - $('.sidebar .this-page-menu li.highlight-link').fadeOut(300); - $('span.highlight').removeClass('highlight'); - }, - - /** - * make the url absolute - */ - makeURL : function(relativeURL) { - return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; - }, - - /** - * get the current relative url - */ - getCurrentURL : function() { - var path = document.location.pathname; - var parts = path.split(/\//); - $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { - if (this == '..') - parts.pop(); - }); - var url = parts.join('/'); - return path.substring(url.lastIndexOf('/') + 1, path.length - 1); - } -}; - -// quick alias for translations -_ = Documentation.gettext; - -$(document).ready(function() { - Documentation.init(); -}); diff --git a/docs/_static/haiku.css b/docs/_static/haiku.css deleted file mode 100644 index 8e28149..0000000 --- a/docs/_static/haiku.css +++ /dev/null @@ -1,386 +0,0 @@ -/* custom stuff I put in FIXME where is it "supposed" to go? */ - -div.admonition-todo -{ - border: 1px solid red; - background-color: #Fdd; -} - -div.admonition-todo p.admonition-title -{ - margin: 0; - color: red; - text-transform: lowercase; -} - -p.admonition-title -{ - font-size: 120%; - font-weight: bold; -} - -dl.class>dt, dl.interface>dt, dl.function>dt, dl.staticmethod>dt -{ - font-size: 150%; - background-color:#ddd; -} - -dl.method>dt -{ - background-color: #eee; - border-bottom: 2px solid #ddd; -} - -dl.method:hover -{ - background-color:#ffd; -} - -/** end custom */ - -html { - margin: 0px; - padding: 0px; - background: #FFF url(bg-page.png) top left repeat-x; -} - -body { - line-height: 1.5; - margin: auto; - padding: 0px; - font-family: "DejaVu Sans", Arial, Helvetica, sans-serif; - min-width: 59em; - max-width: 70em; - color: #333333; -} - -div.footer { - padding: 8px; - font-size: 11px; - text-align: center; - letter-spacing: 0.5px; -} - -/* link colors and text decoration */ - -a:link { - font-weight: bold; - text-decoration: none; - color: #dc3c01; -} - -a:visited { - font-weight: bold; - text-decoration: none; - color: #892601; -} - -a:hover, a:active { - text-decoration: underline; - color: #ff4500; -} - -/* Some headers act as anchors, don't give them a hover effect */ - -h1 a:hover, a:active { - text-decoration: none; - color: #0c3762; -} - -h2 a:hover, a:active { - text-decoration: none; - color: #0c3762; -} - -h3 a:hover, a:active { - text-decoration: none; - color: #0c3762; -} - -h4 a:hover, a:active { - text-decoration: none; - color: #0c3762; -} - -a.headerlink { - color: #a7ce38; - padding-left: 5px; -} - -a.headerlink:hover { - color: #a7ce38; -} - -/* basic text elements */ - -div.content { - margin-top: 20px; - margin-left: 40px; - margin-right: 40px; - margin-bottom: 50px; - font-size: 0.9em; -} - -/* heading and navigation */ - -div.header { - position: relative; - left: 0px; - top: 0px; - height: 85px; - /* background: #eeeeee; */ - padding: 0 40px; -} -div.header h1 { - font-size: 1.6em; - font-weight: normal; - letter-spacing: 1px; - color: #0c3762; - border: 0; - margin: 0; - padding-top: 15px; -} -div.header h1 a { - font-weight: normal; - color: #0c3762; -} -div.header h2 { - font-size: 1.3em; - font-weight: normal; - letter-spacing: 1px; - text-transform: uppercase; - color: #aaa; - border: 0; - margin-top: -3px; - padding: 0; -} - -div.header img.rightlogo { - float: right; -} - - -div.title { - font-size: 1.3em; - font-weight: bold; - color: #0c3762; - border-bottom: dotted thin #e0e0e0; - margin-bottom: 25px; -} -div.topnav { - /* background: #e0e0e0; */ -} -div.topnav p { - margin-top: 0; - margin-left: 40px; - margin-right: 40px; - margin-bottom: 0px; - text-align: right; - font-size: 0.8em; -} -div.bottomnav { - background: #eeeeee; -} -div.bottomnav p { - margin-right: 40px; - text-align: right; - font-size: 0.8em; -} - -a.uplink { - font-weight: normal; -} - - -/* contents box */ - -table.index { - margin: 0px 0px 30px 30px; - padding: 1px; - border-width: 1px; - border-style: dotted; - border-color: #e0e0e0; -} -table.index tr.heading { - background-color: #e0e0e0; - text-align: center; - font-weight: bold; - font-size: 1.1em; -} -table.index tr.index { - background-color: #eeeeee; -} -table.index td { - padding: 5px 20px; -} - -table.index a:link, table.index a:visited { - font-weight: normal; - text-decoration: none; - color: #dc3c01; -} -table.index a:hover, table.index a:active { - text-decoration: underline; - color: #ff4500; -} - - -/* Haiku User Guide styles and layout */ - -/* Rounded corner boxes */ -/* Common declarations */ -div.admonition { - -webkit-border-radius: 10px; - -khtml-border-radius: 10px; - -moz-border-radius: 10px; - border-radius: 10px; - border-style: dotted; - border-width: thin; - border-color: #dcdcdc; - padding: 10px 15px 10px 15px; - margin-bottom: 15px; - margin-top: 15px; -} -div.note { - padding: 10px 15px 10px 80px; - background: #e4ffde url(alert_info_32.png) 15px 15px no-repeat; - min-height: 42px; -} -div.warning { - padding: 10px 15px 10px 80px; - background: #fffbc6 url(alert_warning_32.png) 15px 15px no-repeat; - min-height: 42px; -} -div.seealso { - background: #e4ffde; -} - -/* More layout and styles */ -h1 { - font-size: 1.3em; - font-weight: bold; - color: #0c3762; - border-bottom: dotted thin #e0e0e0; - margin-top: 30px; -} - -h2 { - font-size: 1.2em; - font-weight: normal; - color: #0c3762; - border-bottom: dotted thin #e0e0e0; - margin-top: 30px; -} - -h3 { - font-size: 1.1em; - font-weight: normal; - color: #0c3762; - margin-top: 30px; -} - -h4 { - font-size: 1.0em; - font-weight: normal; - color: #0c3762; - margin-top: 30px; -} - -p { - text-align: justify; -} - -p.last { - margin-bottom: 0; -} - -ol { - padding-left: 20px; -} - -ul { - padding-left: 5px; - margin-top: 3px; -} - -li { - line-height: 1.3; -} - -div.content ul > li { - -moz-background-clip:border; - -moz-background-inline-policy:continuous; - -moz-background-origin:padding; - background: transparent url(bullet_orange.png) no-repeat scroll left 0.45em; - list-style-image: none; - list-style-type: none; - padding: 0 0 0 1.666em; - margin-bottom: 3px; -} - -td { - vertical-align: top; -} - -tt { - background-color: #e2e2e2; - font-size: 1.0em; - font-family: monospace; -} - -pre { - border-color: #0c3762; - border-style: dotted; - border-width: thin; - margin: 0 0 12px 0; - padding: 0.8em; - background-color: #f0f0f0; -} - -hr { - border-top: 1px solid #ccc; - border-bottom: 0; - border-right: 0; - border-left: 0; - margin-bottom: 10px; - margin-top: 20px; -} - -/* printer only pretty stuff */ -@media print { - .noprint { - display: none; - } - /* for acronyms we want their definitions inlined at print time */ - acronym[title]:after { - font-size: small; - content: " (" attr(title) ")"; - font-style: italic; - } - /* and not have mozilla dotted underline */ - acronym { - border: none; - } - div.topnav, div.bottomnav, div.header, table.index { - display: none; - } - div.content { - margin: 0px; - padding: 0px; - } - html { - background: #FFF; - } -} - -.viewcode-back { - font-family: "DejaVu Sans", Arial, Helvetica, sans-serif; -} - -div.viewcode-block:target { - background-color: #f4debf; - border-top: 1px solid #ac9; - border-bottom: 1px solid #ac9; - margin: -1px -12px; - padding: 0 12px; -} diff --git a/docs/_static/jquery.js b/docs/_static/jquery.js deleted file mode 100644 index 1acc8fd..0000000 --- a/docs/_static/jquery.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - * jQuery 1.2.6 - New Wave Javascript - * - * Copyright (c) 2008 John Resig (jquery.com) - * Dual licensed under the MIT (MIT-LICENSE.txt) - * and GPL (GPL-LICENSE.txt) licenses. - * - * $Date: 2008-05-24 14:22:17 -0400 (Sat, 24 May 2008) $ - * $Rev: 5685 $ - */ -(function(){var _jQuery=window.jQuery,_$=window.$;var jQuery=window.jQuery=window.$=function(selector,context){return new jQuery.fn.init(selector,context);};var quickExpr=/^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/,isSimple=/^.[^:#\[\.]*$/,undefined;jQuery.fn=jQuery.prototype={init:function(selector,context){selector=selector||document;if(selector.nodeType){this[0]=selector;this.length=1;return this;}if(typeof selector=="string"){var match=quickExpr.exec(selector);if(match&&(match[1]||!context)){if(match[1])selector=jQuery.clean([match[1]],context);else{var elem=document.getElementById(match[3]);if(elem){if(elem.id!=match[3])return jQuery().find(selector);return jQuery(elem);}selector=[];}}else -return jQuery(context).find(selector);}else if(jQuery.isFunction(selector))return jQuery(document)[jQuery.fn.ready?"ready":"load"](selector);return this.setArray(jQuery.makeArray(selector));},jquery:"1.2.6",size:function(){return this.length;},length:0,get:function(num){return num==undefined?jQuery.makeArray(this):this[num];},pushStack:function(elems){var ret=jQuery(elems);ret.prevObject=this;return ret;},setArray:function(elems){this.length=0;Array.prototype.push.apply(this,elems);return this;},each:function(callback,args){return jQuery.each(this,callback,args);},index:function(elem){var ret=-1;return jQuery.inArray(elem&&elem.jquery?elem[0]:elem,this);},attr:function(name,value,type){var options=name;if(name.constructor==String)if(value===undefined)return this[0]&&jQuery[type||"attr"](this[0],name);else{options={};options[name]=value;}return this.each(function(i){for(name in options)jQuery.attr(type?this.style:this,name,jQuery.prop(this,options[name],type,i,name));});},css:function(key,value){if((key=='width'||key=='height')&&parseFloat(value)<0)value=undefined;return this.attr(key,value,"curCSS");},text:function(text){if(typeof text!="object"&&text!=null)return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text));var ret="";jQuery.each(text||this,function(){jQuery.each(this.childNodes,function(){if(this.nodeType!=8)ret+=this.nodeType!=1?this.nodeValue:jQuery.fn.text([this]);});});return ret;},wrapAll:function(html){if(this[0])jQuery(html,this[0].ownerDocument).clone().insertBefore(this[0]).map(function(){var elem=this;while(elem.firstChild)elem=elem.firstChild;return elem;}).append(this);return this;},wrapInner:function(html){return this.each(function(){jQuery(this).contents().wrapAll(html);});},wrap:function(html){return this.each(function(){jQuery(this).wrapAll(html);});},append:function(){return this.domManip(arguments,true,false,function(elem){if(this.nodeType==1)this.appendChild(elem);});},prepend:function(){return this.domManip(arguments,true,true,function(elem){if(this.nodeType==1)this.insertBefore(elem,this.firstChild);});},before:function(){return this.domManip(arguments,false,false,function(elem){this.parentNode.insertBefore(elem,this);});},after:function(){return this.domManip(arguments,false,true,function(elem){this.parentNode.insertBefore(elem,this.nextSibling);});},end:function(){return this.prevObject||jQuery([]);},find:function(selector){var elems=jQuery.map(this,function(elem){return jQuery.find(selector,elem);});return this.pushStack(/[^+>] [^+>]/.test(selector)||selector.indexOf("..")>-1?jQuery.unique(elems):elems);},clone:function(events){var ret=this.map(function(){if(jQuery.browser.msie&&!jQuery.isXMLDoc(this)){var clone=this.cloneNode(true),container=document.createElement("div");container.appendChild(clone);return jQuery.clean([container.innerHTML])[0];}else -return this.cloneNode(true);});var clone=ret.find("*").andSelf().each(function(){if(this[expando]!=undefined)this[expando]=null;});if(events===true)this.find("*").andSelf().each(function(i){if(this.nodeType==3)return;var events=jQuery.data(this,"events");for(var type in events)for(var handler in events[type])jQuery.event.add(clone[i],type,events[type][handler],events[type][handler].data);});return ret;},filter:function(selector){return this.pushStack(jQuery.isFunction(selector)&&jQuery.grep(this,function(elem,i){return selector.call(elem,i);})||jQuery.multiFilter(selector,this));},not:function(selector){if(selector.constructor==String)if(isSimple.test(selector))return this.pushStack(jQuery.multiFilter(selector,this,true));else -selector=jQuery.multiFilter(selector,this);var isArrayLike=selector.length&&selector[selector.length-1]!==undefined&&!selector.nodeType;return this.filter(function(){return isArrayLike?jQuery.inArray(this,selector)<0:this!=selector;});},add:function(selector){return this.pushStack(jQuery.unique(jQuery.merge(this.get(),typeof selector=='string'?jQuery(selector):jQuery.makeArray(selector))));},is:function(selector){return!!selector&&jQuery.multiFilter(selector,this).length>0;},hasClass:function(selector){return this.is("."+selector);},val:function(value){if(value==undefined){if(this.length){var elem=this[0];if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,values=[],options=elem.options,one=elem.type=="select-one";if(index<0)return null;for(var i=one?index:0,max=one?index+1:options.length;i=0||jQuery.inArray(this.name,value)>=0);else if(jQuery.nodeName(this,"select")){var values=jQuery.makeArray(value);jQuery("option",this).each(function(){this.selected=(jQuery.inArray(this.value,values)>=0||jQuery.inArray(this.text,values)>=0);});if(!values.length)this.selectedIndex=-1;}else -this.value=value;});},html:function(value){return value==undefined?(this[0]?this[0].innerHTML:null):this.empty().append(value);},replaceWith:function(value){return this.after(value).remove();},eq:function(i){return this.slice(i,i+1);},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments));},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem);}));},andSelf:function(){return this.add(this.prevObject);},data:function(key,value){var parts=key.split(".");parts[1]=parts[1]?"."+parts[1]:"";if(value===undefined){var data=this.triggerHandler("getData"+parts[1]+"!",[parts[0]]);if(data===undefined&&this.length)data=jQuery.data(this[0],key);return data===undefined&&parts[1]?this.data(parts[0]):data;}else -return this.trigger("setData"+parts[1]+"!",[parts[0],value]).each(function(){jQuery.data(this,key,value);});},removeData:function(key){return this.each(function(){jQuery.removeData(this,key);});},domManip:function(args,table,reverse,callback){var clone=this.length>1,elems;return this.each(function(){if(!elems){elems=jQuery.clean(args,this.ownerDocument);if(reverse)elems.reverse();}var obj=this;if(table&&jQuery.nodeName(this,"table")&&jQuery.nodeName(elems[0],"tr"))obj=this.getElementsByTagName("tbody")[0]||this.appendChild(this.ownerDocument.createElement("tbody"));var scripts=jQuery([]);jQuery.each(elems,function(){var elem=clone?jQuery(this).clone(true)[0]:this;if(jQuery.nodeName(elem,"script"))scripts=scripts.add(elem);else{if(elem.nodeType==1)scripts=scripts.add(jQuery("script",elem).remove());callback.call(obj,elem);}});scripts.each(evalScript);});}};jQuery.fn.init.prototype=jQuery.fn;function evalScript(i,elem){if(elem.src)jQuery.ajax({url:elem.src,async:false,dataType:"script"});else -jQuery.globalEval(elem.text||elem.textContent||elem.innerHTML||"");if(elem.parentNode)elem.parentNode.removeChild(elem);}function now(){return+new Date;}jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},i=1,length=arguments.length,deep=false,options;if(target.constructor==Boolean){deep=target;target=arguments[1]||{};i=2;}if(typeof target!="object"&&typeof target!="function")target={};if(length==i){target=this;--i;}for(;i-1;}},swap:function(elem,options,callback){var old={};for(var name in options){old[name]=elem.style[name];elem.style[name]=options[name];}callback.call(elem);for(var name in options)elem.style[name]=old[name];},css:function(elem,name,force){if(name=="width"||name=="height"){var val,props={position:"absolute",visibility:"hidden",display:"block"},which=name=="width"?["Left","Right"]:["Top","Bottom"];function getWH(){val=name=="width"?elem.offsetWidth:elem.offsetHeight;var padding=0,border=0;jQuery.each(which,function(){padding+=parseFloat(jQuery.curCSS(elem,"padding"+this,true))||0;border+=parseFloat(jQuery.curCSS(elem,"border"+this+"Width",true))||0;});val-=Math.round(padding+border);}if(jQuery(elem).is(":visible"))getWH();else -jQuery.swap(elem,props,getWH);return Math.max(0,val);}return jQuery.curCSS(elem,name,force);},curCSS:function(elem,name,force){var ret,style=elem.style;function color(elem){if(!jQuery.browser.safari)return false;var ret=defaultView.getComputedStyle(elem,null);return!ret||ret.getPropertyValue("color")=="";}if(name=="opacity"&&jQuery.browser.msie){ret=jQuery.attr(style,"opacity");return ret==""?"1":ret;}if(jQuery.browser.opera&&name=="display"){var save=style.outline;style.outline="0 solid black";style.outline=save;}if(name.match(/float/i))name=styleFloat;if(!force&&style&&style[name])ret=style[name];else if(defaultView.getComputedStyle){if(name.match(/float/i))name="float";name=name.replace(/([A-Z])/g,"-$1").toLowerCase();var computedStyle=defaultView.getComputedStyle(elem,null);if(computedStyle&&!color(elem))ret=computedStyle.getPropertyValue(name);else{var swap=[],stack=[],a=elem,i=0;for(;a&&color(a);a=a.parentNode)stack.unshift(a);for(;i]*?)\/>/g,function(all,front,tag){return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?all:front+">";});var tags=jQuery.trim(elem).toLowerCase(),div=context.createElement("div");var wrap=!tags.indexOf("",""]||!tags.indexOf("",""]||tags.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
"]||!tags.indexOf("",""]||(!tags.indexOf("",""]||!tags.indexOf("",""]||jQuery.browser.msie&&[1,"div
","
"]||[0,"",""];div.innerHTML=wrap[1]+elem+wrap[2];while(wrap[0]--)div=div.lastChild;if(jQuery.browser.msie){var tbody=!tags.indexOf(""&&tags.indexOf("=0;--j)if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length)tbody[j].parentNode.removeChild(tbody[j]);if(/^\s/.test(elem))div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]),div.firstChild);}elem=jQuery.makeArray(div.childNodes);}if(elem.length===0&&(!jQuery.nodeName(elem,"form")&&!jQuery.nodeName(elem,"select")))return;if(elem[0]==undefined||jQuery.nodeName(elem,"form")||elem.options)ret.push(elem);else -ret=jQuery.merge(ret,elem);});return ret;},attr:function(elem,name,value){if(!elem||elem.nodeType==3||elem.nodeType==8)return undefined;var notxml=!jQuery.isXMLDoc(elem),set=value!==undefined,msie=jQuery.browser.msie;name=notxml&&jQuery.props[name]||name;if(elem.tagName){var special=/href|src|style/.test(name);if(name=="selected"&&jQuery.browser.safari)elem.parentNode.selectedIndex;if(name in elem&¬xml&&!special){if(set){if(name=="type"&&jQuery.nodeName(elem,"input")&&elem.parentNode)throw"type property can't be changed";elem[name]=value;}if(jQuery.nodeName(elem,"form")&&elem.getAttributeNode(name))return elem.getAttributeNode(name).nodeValue;return elem[name];}if(msie&¬xml&&name=="style")return jQuery.attr(elem.style,"cssText",value);if(set)elem.setAttribute(name,""+value);var attr=msie&¬xml&&special?elem.getAttribute(name,2):elem.getAttribute(name);return attr===null?undefined:attr;}if(msie&&name=="opacity"){if(set){elem.zoom=1;elem.filter=(elem.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(value)+''=="NaN"?"":"alpha(opacity="+value*100+")");}return elem.filter&&elem.filter.indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity=([^)]*)/)[1])/100)+'':"";}name=name.replace(/-([a-z])/ig,function(all,letter){return letter.toUpperCase();});if(set)elem[name]=value;return elem[name];},trim:function(text){return(text||"").replace(/^\s+|\s+$/g,"");},makeArray:function(array){var ret=[];if(array!=null){var i=array.length;if(i==null||array.split||array.setInterval||array.call)ret[0]=array;else -while(i)ret[--i]=array[i];}return ret;},inArray:function(elem,array){for(var i=0,length=array.length;i*",this).remove();while(this.firstChild)this.removeChild(this.firstChild);}},function(name,fn){jQuery.fn[name]=function(){return this.each(fn,arguments);};});jQuery.each(["Height","Width"],function(i,name){var type=name.toLowerCase();jQuery.fn[type]=function(size){return this[0]==window?jQuery.browser.opera&&document.body["client"+name]||jQuery.browser.safari&&window["inner"+name]||document.compatMode=="CSS1Compat"&&document.documentElement["client"+name]||document.body["client"+name]:this[0]==document?Math.max(Math.max(document.body["scroll"+name],document.documentElement["scroll"+name]),Math.max(document.body["offset"+name],document.documentElement["offset"+name])):size==undefined?(this.length?jQuery.css(this[0],type):null):this.css(type,size.constructor==String?size:size+"px");};});function num(elem,prop){return elem[0]&&parseInt(jQuery.curCSS(elem[0],prop,true),10)||0;}var chars=jQuery.browser.safari&&parseInt(jQuery.browser.version)<417?"(?:[\\w*_-]|\\\\.)":"(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",quickChild=new RegExp("^>\\s*("+chars+"+)"),quickID=new RegExp("^("+chars+"+)(#)("+chars+"+)"),quickClass=new RegExp("^([#.]?)("+chars+"*)");jQuery.extend({expr:{"":function(a,i,m){return m[2]=="*"||jQuery.nodeName(a,m[2]);},"#":function(a,i,m){return a.getAttribute("id")==m[2];},":":{lt:function(a,i,m){return im[3]-0;},nth:function(a,i,m){return m[3]-0==i;},eq:function(a,i,m){return m[3]-0==i;},first:function(a,i){return i==0;},last:function(a,i,m,r){return i==r.length-1;},even:function(a,i){return i%2==0;},odd:function(a,i){return i%2;},"first-child":function(a){return a.parentNode.getElementsByTagName("*")[0]==a;},"last-child":function(a){return jQuery.nth(a.parentNode.lastChild,1,"previousSibling")==a;},"only-child":function(a){return!jQuery.nth(a.parentNode.lastChild,2,"previousSibling");},parent:function(a){return a.firstChild;},empty:function(a){return!a.firstChild;},contains:function(a,i,m){return(a.textContent||a.innerText||jQuery(a).text()||"").indexOf(m[3])>=0;},visible:function(a){return"hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden";},hidden:function(a){return"hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden";},enabled:function(a){return!a.disabled;},disabled:function(a){return a.disabled;},checked:function(a){return a.checked;},selected:function(a){return a.selected||jQuery.attr(a,"selected");},text:function(a){return"text"==a.type;},radio:function(a){return"radio"==a.type;},checkbox:function(a){return"checkbox"==a.type;},file:function(a){return"file"==a.type;},password:function(a){return"password"==a.type;},submit:function(a){return"submit"==a.type;},image:function(a){return"image"==a.type;},reset:function(a){return"reset"==a.type;},button:function(a){return"button"==a.type||jQuery.nodeName(a,"button");},input:function(a){return/input|select|textarea|button/i.test(a.nodeName);},has:function(a,i,m){return jQuery.find(m[3],a).length;},header:function(a){return/h\d/i.test(a.nodeName);},animated:function(a){return jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length;}}},parse:[/^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,new RegExp("^([:.#]*)("+chars+"+)")],multiFilter:function(expr,elems,not){var old,cur=[];while(expr&&expr!=old){old=expr;var f=jQuery.filter(expr,elems,not);expr=f.t.replace(/^\s*,\s*/,"");cur=not?elems=f.r:jQuery.merge(cur,f.r);}return cur;},find:function(t,context){if(typeof t!="string")return[t];if(context&&context.nodeType!=1&&context.nodeType!=9)return[];context=context||document;var ret=[context],done=[],last,nodeName;while(t&&last!=t){var r=[];last=t;t=jQuery.trim(t);var foundToken=false,re=quickChild,m=re.exec(t);if(m){nodeName=m[1].toUpperCase();for(var i=0;ret[i];i++)for(var c=ret[i].firstChild;c;c=c.nextSibling)if(c.nodeType==1&&(nodeName=="*"||c.nodeName.toUpperCase()==nodeName))r.push(c);ret=r;t=t.replace(re,"");if(t.indexOf(" ")==0)continue;foundToken=true;}else{re=/^([>+~])\s*(\w*)/i;if((m=re.exec(t))!=null){r=[];var merge={};nodeName=m[2].toUpperCase();m=m[1];for(var j=0,rl=ret.length;j=0;if(!not&&pass||not&&!pass)tmp.push(r[i]);}return tmp;},filter:function(t,r,not){var last;while(t&&t!=last){last=t;var p=jQuery.parse,m;for(var i=0;p[i];i++){m=p[i].exec(t);if(m){t=t.substring(m[0].length);m[2]=m[2].replace(/\\/g,"");break;}}if(!m)break;if(m[1]==":"&&m[2]=="not")r=isSimple.test(m[3])?jQuery.filter(m[3],r,true).r:jQuery(r).not(m[3]);else if(m[1]==".")r=jQuery.classFilter(r,m[2],not);else if(m[1]=="["){var tmp=[],type=m[3];for(var i=0,rl=r.length;i=0)^not)tmp.push(a);}r=tmp;}else if(m[1]==":"&&m[2]=="nth-child"){var merge={},tmp=[],test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[3]=="even"&&"2n"||m[3]=="odd"&&"2n+1"||!/\D/.test(m[3])&&"0n+"+m[3]||m[3]),first=(test[1]+(test[2]||1))-0,last=test[3]-0;for(var i=0,rl=r.length;i=0)add=true;if(add^not)tmp.push(node);}r=tmp;}else{var fn=jQuery.expr[m[1]];if(typeof fn=="object")fn=fn[m[2]];if(typeof fn=="string")fn=eval("false||function(a,i){return "+fn+";}");r=jQuery.grep(r,function(elem,i){return fn(elem,i,m,r);},not);}}return{r:r,t:t};},dir:function(elem,dir){var matched=[],cur=elem[dir];while(cur&&cur!=document){if(cur.nodeType==1)matched.push(cur);cur=cur[dir];}return matched;},nth:function(cur,result,dir,elem){result=result||1;var num=0;for(;cur;cur=cur[dir])if(cur.nodeType==1&&++num==result)break;return cur;},sibling:function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType==1&&n!=elem)r.push(n);}return r;}});jQuery.event={add:function(elem,types,handler,data){if(elem.nodeType==3||elem.nodeType==8)return;if(jQuery.browser.msie&&elem.setInterval)elem=window;if(!handler.guid)handler.guid=this.guid++;if(data!=undefined){var fn=handler;handler=this.proxy(fn,function(){return fn.apply(this,arguments);});handler.data=data;}var events=jQuery.data(elem,"events")||jQuery.data(elem,"events",{}),handle=jQuery.data(elem,"handle")||jQuery.data(elem,"handle",function(){if(typeof jQuery!="undefined"&&!jQuery.event.triggered)return jQuery.event.handle.apply(arguments.callee.elem,arguments);});handle.elem=elem;jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];handler.type=parts[1];var handlers=events[type];if(!handlers){handlers=events[type]={};if(!jQuery.event.special[type]||jQuery.event.special[type].setup.call(elem)===false){if(elem.addEventListener)elem.addEventListener(type,handle,false);else if(elem.attachEvent)elem.attachEvent("on"+type,handle);}}handlers[handler.guid]=handler;jQuery.event.global[type]=true;});elem=null;},guid:1,global:{},remove:function(elem,types,handler){if(elem.nodeType==3||elem.nodeType==8)return;var events=jQuery.data(elem,"events"),ret,index;if(events){if(types==undefined||(typeof types=="string"&&types.charAt(0)=="."))for(var type in events)this.remove(elem,type+(types||""));else{if(types.type){handler=types.handler;types=types.type;}jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];if(events[type]){if(handler)delete events[type][handler.guid];else -for(handler in events[type])if(!parts[1]||events[type][handler].type==parts[1])delete events[type][handler];for(ret in events[type])break;if(!ret){if(!jQuery.event.special[type]||jQuery.event.special[type].teardown.call(elem)===false){if(elem.removeEventListener)elem.removeEventListener(type,jQuery.data(elem,"handle"),false);else if(elem.detachEvent)elem.detachEvent("on"+type,jQuery.data(elem,"handle"));}ret=null;delete events[type];}}});}for(ret in events)break;if(!ret){var handle=jQuery.data(elem,"handle");if(handle)handle.elem=null;jQuery.removeData(elem,"events");jQuery.removeData(elem,"handle");}}},trigger:function(type,data,elem,donative,extra){data=jQuery.makeArray(data);if(type.indexOf("!")>=0){type=type.slice(0,-1);var exclusive=true;}if(!elem){if(this.global[type])jQuery("*").add([window,document]).trigger(type,data);}else{if(elem.nodeType==3||elem.nodeType==8)return undefined;var val,ret,fn=jQuery.isFunction(elem[type]||null),event=!data[0]||!data[0].preventDefault;if(event){data.unshift({type:type,target:elem,preventDefault:function(){},stopPropagation:function(){},timeStamp:now()});data[0][expando]=true;}data[0].type=type;if(exclusive)data[0].exclusive=true;var handle=jQuery.data(elem,"handle");if(handle)val=handle.apply(elem,data);if((!fn||(jQuery.nodeName(elem,'a')&&type=="click"))&&elem["on"+type]&&elem["on"+type].apply(elem,data)===false)val=false;if(event)data.shift();if(extra&&jQuery.isFunction(extra)){ret=extra.apply(elem,val==null?data:data.concat(val));if(ret!==undefined)val=ret;}if(fn&&donative!==false&&val!==false&&!(jQuery.nodeName(elem,'a')&&type=="click")){this.triggered=true;try{elem[type]();}catch(e){}}this.triggered=false;}return val;},handle:function(event){var val,ret,namespace,all,handlers;event=arguments[0]=jQuery.event.fix(event||window.event);namespace=event.type.split(".");event.type=namespace[0];namespace=namespace[1];all=!namespace&&!event.exclusive;handlers=(jQuery.data(this,"events")||{})[event.type];for(var j in handlers){var handler=handlers[j];if(all||handler.type==namespace){event.handler=handler;event.data=handler.data;ret=handler.apply(this,arguments);if(val!==false)val=ret;if(ret===false){event.preventDefault();event.stopPropagation();}}}return val;},fix:function(event){if(event[expando]==true)return event;var originalEvent=event;event={originalEvent:originalEvent};var props="altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target timeStamp toElement type view wheelDelta which".split(" ");for(var i=props.length;i;i--)event[props[i]]=originalEvent[props[i]];event[expando]=true;event.preventDefault=function(){if(originalEvent.preventDefault)originalEvent.preventDefault();originalEvent.returnValue=false;};event.stopPropagation=function(){if(originalEvent.stopPropagation)originalEvent.stopPropagation();originalEvent.cancelBubble=true;};event.timeStamp=event.timeStamp||now();if(!event.target)event.target=event.srcElement||document;if(event.target.nodeType==3)event.target=event.target.parentNode;if(!event.relatedTarget&&event.fromElement)event.relatedTarget=event.fromElement==event.target?event.toElement:event.fromElement;if(event.pageX==null&&event.clientX!=null){var doc=document.documentElement,body=document.body;event.pageX=event.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc.clientLeft||0);event.pageY=event.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc.clientTop||0);}if(!event.which&&((event.charCode||event.charCode===0)?event.charCode:event.keyCode))event.which=event.charCode||event.keyCode;if(!event.metaKey&&event.ctrlKey)event.metaKey=event.ctrlKey;if(!event.which&&event.button)event.which=(event.button&1?1:(event.button&2?3:(event.button&4?2:0)));return event;},proxy:function(fn,proxy){proxy.guid=fn.guid=fn.guid||proxy.guid||this.guid++;return proxy;},special:{ready:{setup:function(){bindReady();return;},teardown:function(){return;}},mouseenter:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseover",jQuery.event.special.mouseenter.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseover",jQuery.event.special.mouseenter.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseenter";return jQuery.event.handle.apply(this,arguments);}},mouseleave:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseout",jQuery.event.special.mouseleave.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseout",jQuery.event.special.mouseleave.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseleave";return jQuery.event.handle.apply(this,arguments);}}}};jQuery.fn.extend({bind:function(type,data,fn){return type=="unload"?this.one(type,data,fn):this.each(function(){jQuery.event.add(this,type,fn||data,fn&&data);});},one:function(type,data,fn){var one=jQuery.event.proxy(fn||data,function(event){jQuery(this).unbind(event,one);return(fn||data).apply(this,arguments);});return this.each(function(){jQuery.event.add(this,type,one,fn&&data);});},unbind:function(type,fn){return this.each(function(){jQuery.event.remove(this,type,fn);});},trigger:function(type,data,fn){return this.each(function(){jQuery.event.trigger(type,data,this,true,fn);});},triggerHandler:function(type,data,fn){return this[0]&&jQuery.event.trigger(type,data,this[0],false,fn);},toggle:function(fn){var args=arguments,i=1;while(i=0){var selector=url.slice(off,url.length);url=url.slice(0,off);}callback=callback||function(){};var type="GET";if(params)if(jQuery.isFunction(params)){callback=params;params=null;}else{params=jQuery.param(params);type="POST";}var self=this;jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(res,status){if(status=="success"||status=="notmodified")self.html(selector?jQuery("
").append(res.responseText.replace(//g,"")).find(selector):res.responseText);self.each(callback,[res.responseText,status,res]);}});return this;},serialize:function(){return jQuery.param(this.serializeArray());},serializeArray:function(){return this.map(function(){return jQuery.nodeName(this,"form")?jQuery.makeArray(this.elements):this;}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password/i.test(this.type));}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:val.constructor==Array?jQuery.map(val,function(val,i){return{name:elem.name,value:val};}):{name:elem.name,value:val};}).get();}});jQuery.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(i,o){jQuery.fn[o]=function(f){return this.bind(o,f);};});var jsc=now();jQuery.extend({get:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data=null;}return jQuery.ajax({type:"GET",url:url,data:data,success:callback,dataType:type});},getScript:function(url,callback){return jQuery.get(url,null,callback,"script");},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json");},post:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data={};}return jQuery.ajax({type:"POST",url:url,data:data,success:callback,dataType:type});},ajaxSetup:function(settings){jQuery.extend(jQuery.ajaxSettings,settings);},ajaxSettings:{url:location.href,global:true,type:"GET",timeout:0,contentType:"application/x-www-form-urlencoded",processData:true,async:true,data:null,username:null,password:null,accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(s){s=jQuery.extend(true,s,jQuery.extend(true,{},jQuery.ajaxSettings,s));var jsonp,jsre=/=\?(&|$)/g,status,data,type=s.type.toUpperCase();if(s.data&&s.processData&&typeof s.data!="string")s.data=jQuery.param(s.data);if(s.dataType=="jsonp"){if(type=="GET"){if(!s.url.match(jsre))s.url+=(s.url.match(/\?/)?"&":"?")+(s.jsonp||"callback")+"=?";}else if(!s.data||!s.data.match(jsre))s.data=(s.data?s.data+"&":"")+(s.jsonp||"callback")+"=?";s.dataType="json";}if(s.dataType=="json"&&(s.data&&s.data.match(jsre)||s.url.match(jsre))){jsonp="jsonp"+jsc++;if(s.data)s.data=(s.data+"").replace(jsre,"="+jsonp+"$1");s.url=s.url.replace(jsre,"="+jsonp+"$1");s.dataType="script";window[jsonp]=function(tmp){data=tmp;success();complete();window[jsonp]=undefined;try{delete window[jsonp];}catch(e){}if(head)head.removeChild(script);};}if(s.dataType=="script"&&s.cache==null)s.cache=false;if(s.cache===false&&type=="GET"){var ts=now();var ret=s.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+ts+"$2");s.url=ret+((ret==s.url)?(s.url.match(/\?/)?"&":"?")+"_="+ts:"");}if(s.data&&type=="GET"){s.url+=(s.url.match(/\?/)?"&":"?")+s.data;s.data=null;}if(s.global&&!jQuery.active++)jQuery.event.trigger("ajaxStart");var remote=/^(?:\w+:)?\/\/([^\/?#]+)/;if(s.dataType=="script"&&type=="GET"&&remote.test(s.url)&&remote.exec(s.url)[1]!=location.host){var head=document.getElementsByTagName("head")[0];var script=document.createElement("script");script.src=s.url;if(s.scriptCharset)script.charset=s.scriptCharset;if(!jsonp){var done=false;script.onload=script.onreadystatechange=function(){if(!done&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){done=true;success();complete();head.removeChild(script);}};}head.appendChild(script);return undefined;}var requestDone=false;var xhr=window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();if(s.username)xhr.open(type,s.url,s.async,s.username,s.password);else -xhr.open(type,s.url,s.async);try{if(s.data)xhr.setRequestHeader("Content-Type",s.contentType);if(s.ifModified)xhr.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]||"Thu, 01 Jan 1970 00:00:00 GMT");xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");xhr.setRequestHeader("Accept",s.dataType&&s.accepts[s.dataType]?s.accepts[s.dataType]+", */*":s.accepts._default);}catch(e){}if(s.beforeSend&&s.beforeSend(xhr,s)===false){s.global&&jQuery.active--;xhr.abort();return false;}if(s.global)jQuery.event.trigger("ajaxSend",[xhr,s]);var onreadystatechange=function(isTimeout){if(!requestDone&&xhr&&(xhr.readyState==4||isTimeout=="timeout")){requestDone=true;if(ival){clearInterval(ival);ival=null;}status=isTimeout=="timeout"&&"timeout"||!jQuery.httpSuccess(xhr)&&"error"||s.ifModified&&jQuery.httpNotModified(xhr,s.url)&&"notmodified"||"success";if(status=="success"){try{data=jQuery.httpData(xhr,s.dataType,s.dataFilter);}catch(e){status="parsererror";}}if(status=="success"){var modRes;try{modRes=xhr.getResponseHeader("Last-Modified");}catch(e){}if(s.ifModified&&modRes)jQuery.lastModified[s.url]=modRes;if(!jsonp)success();}else -jQuery.handleError(s,xhr,status);complete();if(s.async)xhr=null;}};if(s.async){var ival=setInterval(onreadystatechange,13);if(s.timeout>0)setTimeout(function(){if(xhr){xhr.abort();if(!requestDone)onreadystatechange("timeout");}},s.timeout);}try{xhr.send(s.data);}catch(e){jQuery.handleError(s,xhr,null,e);}if(!s.async)onreadystatechange();function success(){if(s.success)s.success(data,status);if(s.global)jQuery.event.trigger("ajaxSuccess",[xhr,s]);}function complete(){if(s.complete)s.complete(xhr,status);if(s.global)jQuery.event.trigger("ajaxComplete",[xhr,s]);if(s.global&&!--jQuery.active)jQuery.event.trigger("ajaxStop");}return xhr;},handleError:function(s,xhr,status,e){if(s.error)s.error(xhr,status,e);if(s.global)jQuery.event.trigger("ajaxError",[xhr,s,e]);},active:0,httpSuccess:function(xhr){try{return!xhr.status&&location.protocol=="file:"||(xhr.status>=200&&xhr.status<300)||xhr.status==304||xhr.status==1223||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpNotModified:function(xhr,url){try{var xhrRes=xhr.getResponseHeader("Last-Modified");return xhr.status==304||xhrRes==jQuery.lastModified[url]||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpData:function(xhr,type,filter){var ct=xhr.getResponseHeader("content-type"),xml=type=="xml"||!type&&ct&&ct.indexOf("xml")>=0,data=xml?xhr.responseXML:xhr.responseText;if(xml&&data.documentElement.tagName=="parsererror")throw"parsererror";if(filter)data=filter(data,type);if(type=="script")jQuery.globalEval(data);if(type=="json")data=eval("("+data+")");return data;},param:function(a){var s=[];if(a.constructor==Array||a.jquery)jQuery.each(a,function(){s.push(encodeURIComponent(this.name)+"="+encodeURIComponent(this.value));});else -for(var j in a)if(a[j]&&a[j].constructor==Array)jQuery.each(a[j],function(){s.push(encodeURIComponent(j)+"="+encodeURIComponent(this));});else -s.push(encodeURIComponent(j)+"="+encodeURIComponent(jQuery.isFunction(a[j])?a[j]():a[j]));return s.join("&").replace(/%20/g,"+");}});jQuery.fn.extend({show:function(speed,callback){return speed?this.animate({height:"show",width:"show",opacity:"show"},speed,callback):this.filter(":hidden").each(function(){this.style.display=this.oldblock||"";if(jQuery.css(this,"display")=="none"){var elem=jQuery("<"+this.tagName+" />").appendTo("body");this.style.display=elem.css("display");if(this.style.display=="none")this.style.display="block";elem.remove();}}).end();},hide:function(speed,callback){return speed?this.animate({height:"hide",width:"hide",opacity:"hide"},speed,callback):this.filter(":visible").each(function(){this.oldblock=this.oldblock||jQuery.css(this,"display");this.style.display="none";}).end();},_toggle:jQuery.fn.toggle,toggle:function(fn,fn2){return jQuery.isFunction(fn)&&jQuery.isFunction(fn2)?this._toggle.apply(this,arguments):fn?this.animate({height:"toggle",width:"toggle",opacity:"toggle"},fn,fn2):this.each(function(){jQuery(this)[jQuery(this).is(":hidden")?"show":"hide"]();});},slideDown:function(speed,callback){return this.animate({height:"show"},speed,callback);},slideUp:function(speed,callback){return this.animate({height:"hide"},speed,callback);},slideToggle:function(speed,callback){return this.animate({height:"toggle"},speed,callback);},fadeIn:function(speed,callback){return this.animate({opacity:"show"},speed,callback);},fadeOut:function(speed,callback){return this.animate({opacity:"hide"},speed,callback);},fadeTo:function(speed,to,callback){return this.animate({opacity:to},speed,callback);},animate:function(prop,speed,easing,callback){var optall=jQuery.speed(speed,easing,callback);return this[optall.queue===false?"each":"queue"](function(){if(this.nodeType!=1)return false;var opt=jQuery.extend({},optall),p,hidden=jQuery(this).is(":hidden"),self=this;for(p in prop){if(prop[p]=="hide"&&hidden||prop[p]=="show"&&!hidden)return opt.complete.call(this);if(p=="height"||p=="width"){opt.display=jQuery.css(this,"display");opt.overflow=this.style.overflow;}}if(opt.overflow!=null)this.style.overflow="hidden";opt.curAnim=jQuery.extend({},prop);jQuery.each(prop,function(name,val){var e=new jQuery.fx(self,opt,name);if(/toggle|show|hide/.test(val))e[val=="toggle"?hidden?"show":"hide":val](prop);else{var parts=val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),start=e.cur(true)||0;if(parts){var end=parseFloat(parts[2]),unit=parts[3]||"px";if(unit!="px"){self.style[name]=(end||1)+unit;start=((end||1)/e.cur(true))*start;self.style[name]=start+unit;}if(parts[1])end=((parts[1]=="-="?-1:1)*end)+start;e.custom(start,end,unit);}else -e.custom(start,val,"");}});return true;});},queue:function(type,fn){if(jQuery.isFunction(type)||(type&&type.constructor==Array)){fn=type;type="fx";}if(!type||(typeof type=="string"&&!fn))return queue(this[0],type);return this.each(function(){if(fn.constructor==Array)queue(this,type,fn);else{queue(this,type).push(fn);if(queue(this,type).length==1)fn.call(this);}});},stop:function(clearQueue,gotoEnd){var timers=jQuery.timers;if(clearQueue)this.queue([]);this.each(function(){for(var i=timers.length-1;i>=0;i--)if(timers[i].elem==this){if(gotoEnd)timers[i](true);timers.splice(i,1);}});if(!gotoEnd)this.dequeue();return this;}});var queue=function(elem,type,array){if(elem){type=type||"fx";var q=jQuery.data(elem,type+"queue");if(!q||array)q=jQuery.data(elem,type+"queue",jQuery.makeArray(array));}return q;};jQuery.fn.dequeue=function(type){type=type||"fx";return this.each(function(){var q=queue(this,type);q.shift();if(q.length)q[0].call(this);});};jQuery.extend({speed:function(speed,easing,fn){var opt=speed&&speed.constructor==Object?speed:{complete:fn||!fn&&easing||jQuery.isFunction(speed)&&speed,duration:speed,easing:fn&&easing||easing&&easing.constructor!=Function&&easing};opt.duration=(opt.duration&&opt.duration.constructor==Number?opt.duration:jQuery.fx.speeds[opt.duration])||jQuery.fx.speeds.def;opt.old=opt.complete;opt.complete=function(){if(opt.queue!==false)jQuery(this).dequeue();if(jQuery.isFunction(opt.old))opt.old.call(this);};return opt;},easing:{linear:function(p,n,firstNum,diff){return firstNum+diff*p;},swing:function(p,n,firstNum,diff){return((-Math.cos(p*Math.PI)/2)+0.5)*diff+firstNum;}},timers:[],timerId:null,fx:function(elem,options,prop){this.options=options;this.elem=elem;this.prop=prop;if(!options.orig)options.orig={};}});jQuery.fx.prototype={update:function(){if(this.options.step)this.options.step.call(this.elem,this.now,this);(jQuery.fx.step[this.prop]||jQuery.fx.step._default)(this);if(this.prop=="height"||this.prop=="width")this.elem.style.display="block";},cur:function(force){if(this.elem[this.prop]!=null&&this.elem.style[this.prop]==null)return this.elem[this.prop];var r=parseFloat(jQuery.css(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.curCSS(this.elem,this.prop))||0;},custom:function(from,to,unit){this.startTime=now();this.start=from;this.end=to;this.unit=unit||this.unit||"px";this.now=this.start;this.pos=this.state=0;this.update();var self=this;function t(gotoEnd){return self.step(gotoEnd);}t.elem=this.elem;jQuery.timers.push(t);if(jQuery.timerId==null){jQuery.timerId=setInterval(function(){var timers=jQuery.timers;for(var i=0;ithis.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var done=true;for(var i in this.options.curAnim)if(this.options.curAnim[i]!==true)done=false;if(done){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(jQuery.css(this.elem,"display")=="none")this.elem.style.display="block";}if(this.options.hide)this.elem.style.display="none";if(this.options.hide||this.options.show)for(var p in this.options.curAnim)jQuery.attr(this.elem.style,p,this.options.orig[p]);}if(done)this.options.complete.call(this.elem);return false;}else{var n=t-this.startTime;this.state=n/this.options.duration;this.pos=jQuery.easing[this.options.easing||(jQuery.easing.swing?"swing":"linear")](this.state,n,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update();}return true;}};jQuery.extend(jQuery.fx,{speeds:{slow:600,fast:200,def:400},step:{scrollLeft:function(fx){fx.elem.scrollLeft=fx.now;},scrollTop:function(fx){fx.elem.scrollTop=fx.now;},opacity:function(fx){jQuery.attr(fx.elem.style,"opacity",fx.now);},_default:function(fx){fx.elem.style[fx.prop]=fx.now+fx.unit;}}});jQuery.fn.offset=function(){var left=0,top=0,elem=this[0],results;if(elem)with(jQuery.browser){var parent=elem.parentNode,offsetChild=elem,offsetParent=elem.offsetParent,doc=elem.ownerDocument,safari2=safari&&parseInt(version)<522&&!/adobeair/i.test(userAgent),css=jQuery.curCSS,fixed=css(elem,"position")=="fixed";if(elem.getBoundingClientRect){var box=elem.getBoundingClientRect();add(box.left+Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),box.top+Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));add(-doc.documentElement.clientLeft,-doc.documentElement.clientTop);}else{add(elem.offsetLeft,elem.offsetTop);while(offsetParent){add(offsetParent.offsetLeft,offsetParent.offsetTop);if(mozilla&&!/^t(able|d|h)$/i.test(offsetParent.tagName)||safari&&!safari2)border(offsetParent);if(!fixed&&css(offsetParent,"position")=="fixed")fixed=true;offsetChild=/^body$/i.test(offsetParent.tagName)?offsetChild:offsetParent;offsetParent=offsetParent.offsetParent;}while(parent&&parent.tagName&&!/^body|html$/i.test(parent.tagName)){if(!/^inline|table.*$/i.test(css(parent,"display")))add(-parent.scrollLeft,-parent.scrollTop);if(mozilla&&css(parent,"overflow")!="visible")border(parent);parent=parent.parentNode;}if((safari2&&(fixed||css(offsetChild,"position")=="absolute"))||(mozilla&&css(offsetChild,"position")!="absolute"))add(-doc.body.offsetLeft,-doc.body.offsetTop);if(fixed)add(Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));}results={top:top,left:left};}function border(elem){add(jQuery.curCSS(elem,"borderLeftWidth",true),jQuery.curCSS(elem,"borderTopWidth",true));}function add(l,t){left+=parseInt(l,10)||0;top+=parseInt(t,10)||0;}return results;};jQuery.fn.extend({position:function(){var left=0,top=0,results;if(this[0]){var offsetParent=this.offsetParent(),offset=this.offset(),parentOffset=/^body|html$/i.test(offsetParent[0].tagName)?{top:0,left:0}:offsetParent.offset();offset.top-=num(this,'marginTop');offset.left-=num(this,'marginLeft');parentOffset.top+=num(offsetParent,'borderTopWidth');parentOffset.left+=num(offsetParent,'borderLeftWidth');results={top:offset.top-parentOffset.top,left:offset.left-parentOffset.left};}return results;},offsetParent:function(){var offsetParent=this[0].offsetParent;while(offsetParent&&(!/^body|html$/i.test(offsetParent.tagName)&&jQuery.css(offsetParent,'position')=='static'))offsetParent=offsetParent.offsetParent;return jQuery(offsetParent);}});jQuery.each(['Left','Top'],function(i,name){var method='scroll'+name;jQuery.fn[method]=function(val){if(!this[0])return;return val!=undefined?this.each(function(){this==window||this==document?window.scrollTo(!i?val:jQuery(window).scrollLeft(),i?val:jQuery(window).scrollTop()):this[method]=val;}):this[0]==window||this[0]==document?self[i?'pageYOffset':'pageXOffset']||jQuery.boxModel&&document.documentElement[method]||document.body[method]:this[0][method];};});jQuery.each(["Height","Width"],function(i,name){var tl=i?"Left":"Top",br=i?"Right":"Bottom";jQuery.fn["inner"+name]=function(){return this[name.toLowerCase()]()+num(this,"padding"+tl)+num(this,"padding"+br);};jQuery.fn["outer"+name]=function(margin){return this["inner"+name]()+num(this,"border"+tl+"Width")+num(this,"border"+br+"Width")+(margin?num(this,"margin"+tl)+num(this,"margin"+br):0);};});})(); diff --git a/docs/_static/pygments.css b/docs/_static/pygments.css deleted file mode 100644 index 45bae6e..0000000 --- a/docs/_static/pygments.css +++ /dev/null @@ -1,69 +0,0 @@ -.hll { background-color: #ffffcc } -.c { color: #8f5902; font-style: italic } /* Comment */ -.err { color: #a40000; border: 1px solid #ef2929 } /* Error */ -.g { color: #000000 } /* Generic */ -.k { color: #204a87; font-weight: bold } /* Keyword */ -.l { color: #000000 } /* Literal */ -.n { color: #000000 } /* Name */ -.o { color: #ce5c00; font-weight: bold } /* Operator */ -.x { color: #000000 } /* Other */ -.p { color: #000000; font-weight: bold } /* Punctuation */ -.cm { color: #8f5902; font-style: italic } /* Comment.Multiline */ -.cp { color: #8f5902; font-style: italic } /* Comment.Preproc */ -.c1 { color: #8f5902; font-style: italic } /* Comment.Single */ -.cs { color: #8f5902; font-style: italic } /* Comment.Special */ -.gd { color: #a40000 } /* Generic.Deleted */ -.ge { color: #000000; font-style: italic } /* Generic.Emph */ -.gr { color: #ef2929 } /* Generic.Error */ -.gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.gi { color: #00A000 } /* Generic.Inserted */ -.go { color: #000000; font-style: italic } /* Generic.Output */ -.gp { color: #8f5902 } /* Generic.Prompt */ -.gs { color: #000000; font-weight: bold } /* Generic.Strong */ -.gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.gt { color: #a40000; font-weight: bold } /* Generic.Traceback */ -.kc { color: #204a87; font-weight: bold } /* Keyword.Constant */ -.kd { color: #204a87; font-weight: bold } /* Keyword.Declaration */ -.kn { color: #204a87; font-weight: bold } /* Keyword.Namespace */ -.kp { color: #204a87; font-weight: bold } /* Keyword.Pseudo */ -.kr { color: #204a87; font-weight: bold } /* Keyword.Reserved */ -.kt { color: #204a87; font-weight: bold } /* Keyword.Type */ -.ld { color: #000000 } /* Literal.Date */ -.m { color: #0000cf; font-weight: bold } /* Literal.Number */ -.s { color: #4e9a06 } /* Literal.String */ -.na { color: #c4a000 } /* Name.Attribute */ -.nb { color: #204a87 } /* Name.Builtin */ -.nc { color: #000000 } /* Name.Class */ -.no { color: #000000 } /* Name.Constant */ -.nd { color: #5c35cc; font-weight: bold } /* Name.Decorator */ -.ni { color: #ce5c00 } /* Name.Entity */ -.ne { color: #cc0000; font-weight: bold } /* Name.Exception */ -.nf { color: #000000 } /* Name.Function */ -.nl { color: #f57900 } /* Name.Label */ -.nn { color: #000000 } /* Name.Namespace */ -.nx { color: #000000 } /* Name.Other */ -.py { color: #000000 } /* Name.Property */ -.nt { color: #204a87; font-weight: bold } /* Name.Tag */ -.nv { color: #000000 } /* Name.Variable */ -.ow { color: #204a87; font-weight: bold } /* Operator.Word */ -.w { color: #f8f8f8; text-decoration: underline } /* Text.Whitespace */ -.mf { color: #0000cf; font-weight: bold } /* Literal.Number.Float */ -.mh { color: #0000cf; font-weight: bold } /* Literal.Number.Hex */ -.mi { color: #0000cf; font-weight: bold } /* Literal.Number.Integer */ -.mo { color: #0000cf; font-weight: bold } /* Literal.Number.Oct */ -.sb { color: #4e9a06 } /* Literal.String.Backtick */ -.sc { color: #4e9a06 } /* Literal.String.Char */ -.sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */ -.s2 { color: #4e9a06 } /* Literal.String.Double */ -.se { color: #4e9a06 } /* Literal.String.Escape */ -.sh { color: #4e9a06 } /* Literal.String.Heredoc */ -.si { color: #4e9a06 } /* Literal.String.Interpol */ -.sx { color: #4e9a06 } /* Literal.String.Other */ -.sr { color: #4e9a06 } /* Literal.String.Regex */ -.s1 { color: #4e9a06 } /* Literal.String.Single */ -.ss { color: #4e9a06 } /* Literal.String.Symbol */ -.bp { color: #3465a4 } /* Name.Builtin.Pseudo */ -.vc { color: #000000 } /* Name.Variable.Class */ -.vg { color: #000000 } /* Name.Variable.Global */ -.vi { color: #000000 } /* Name.Variable.Instance */ -.il { color: #0000cf; font-weight: bold } /* Literal.Number.Integer.Long */ From e6944488fbfe5af869450bff6b0cb7f9c138ec02 Mon Sep 17 00:00:00 2001 From: drebs Date: Mon, 14 Oct 2013 14:35:01 -0300 Subject: [PATCH 05/56] Replace os.getresuid() so it works on Mac OS X. --- gnupg/_meta.py | 2 +- gnupg/_util.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/gnupg/_meta.py b/gnupg/_meta.py index 57a0dfb..03b1012 100644 --- a/gnupg/_meta.py +++ b/gnupg/_meta.py @@ -75,7 +75,7 @@ class GPGMeta(type): same effective user ID as that of this program. Otherwise, returns None. """ - identity = os.getresuid() + identity = psutil.Process(os.getpid()).uids for proc in psutil.process_iter(): if (proc.name == "gpg-agent") and proc.is_running: log.debug("Found gpg-agent process with pid %d" % proc.pid) diff --git a/gnupg/_util.py b/gnupg/_util.py index dd80632..108a009 100644 --- a/gnupg/_util.py +++ b/gnupg/_util.py @@ -31,6 +31,7 @@ from time import mktime import codecs import encodings import os +import psutil import threading import random import re @@ -393,7 +394,7 @@ def _make_passphrase(length=None, save=False, file=None): passphrase = _make_random_string(length) if save: - ruid, euid, suid = os.getresuid() + ruid, euid, suid = psutil.Process(os.getpid()).uids gid = os.getgid() now = mktime(localtime()) From 678d36ea620a81c16a0cdacd7f1955795abd02a1 Mon Sep 17 00:00:00 2001 From: drebs Date: Mon, 14 Oct 2013 17:09:09 -0300 Subject: [PATCH 06/56] Fix path creation. --- gnupg/gnupg.py | 10 ++++++---- gnupg/test/test_gnupg.py | 3 +-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/gnupg/gnupg.py b/gnupg/gnupg.py index 0605f68..489e652 100644 --- a/gnupg/gnupg.py +++ b/gnupg/gnupg.py @@ -557,18 +557,20 @@ class GPG(GPGBase): fpr = str(key.fingerprint) if len(fpr) == 20: - if self.temp_keyring or self.temp_secring: - if not os.path.exists(self._keys_dir): - os.makedirs(self._keys_dir) - prefix = os.path.join(self._keys_dir, fpr) + for d in map(lambda x: os.path.dirname(x), + [self.temp_keyring, self.temp_secring]): + if not os.path.exists(d): + os.makedirs(d) if self.temp_keyring: if os.path.isfile(self.temp_keyring): + prefix = os.path.join(self.temp_keyring, fpr) try: os.rename(self.temp_keyring, prefix+".pubring") except OSError as ose: log.error(ose.message) if self.temp_secring: if os.path.isfile(self.temp_secring): + prefix = os.path.join(self.temp_secring, fpr) try: os.rename(self.temp_secring, prefix+".secring") except OSError as ose: log.error(ose.message) diff --git a/gnupg/test/test_gnupg.py b/gnupg/test/test_gnupg.py index 6bf2c8a..e223dbc 100644 --- a/gnupg/test/test_gnupg.py +++ b/gnupg/test/test_gnupg.py @@ -173,7 +173,6 @@ class GPGTestCase(unittest.TestCase): self.keyring = self.gpg.keyring self.secring = self.gpg.secring self.insecure_prng = False - self.gpg._keys_dir = os.path.join(_files, 'generated-keys') def tearDown(self): """This is called once per self.test_* method after the test run.""" @@ -523,7 +522,7 @@ class GPGTestCase(unittest.TestCase): self.assertIsNotNone(key) self.assertNotEquals(key, "") self.assertGreater(len(str(key)), 0) - keyfile = os.path.join(self.gpg._keys_dir, 'test_key_3.pub') + keyfile = os.path.join(_files, 'test_key_3.pub') log.debug("Storing downloaded key as %s" % keyfile) with open(keyfile, 'w') as fh: fh.write(str(key)) From 2c0c826c0f55864a39f7c92f4b88b108ebd513fa Mon Sep 17 00:00:00 2001 From: drebs Date: Thu, 24 Oct 2013 18:04:30 -0200 Subject: [PATCH 07/56] Fix gpg binary path validation. --- gnupg/_util.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gnupg/_util.py b/gnupg/_util.py index 108a009..28b8280 100644 --- a/gnupg/_util.py +++ b/gnupg/_util.py @@ -271,6 +271,8 @@ def _find_binary(binary=None): except IndexError as ie: log.info("Could not determine absolute path of binary: '%s'" % binary) + elif os.access(binary, os.X_OK): + found = binary if found is None: try: found = _which('gpg')[0] except IndexError as ie: From 2f60144ad94df1291bb59fe4cf0fa61093020f79 Mon Sep 17 00:00:00 2001 From: drebs Date: Thu, 24 Oct 2013 18:05:18 -0200 Subject: [PATCH 08/56] Fix validation of --verify argument using detached sig. --- gnupg/_parsers.py | 12 ++++++++---- gnupg/gnupg.py | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/gnupg/_parsers.py b/gnupg/_parsers.py index e409afc..1c99c0d 100644 --- a/gnupg/_parsers.py +++ b/gnupg/_parsers.py @@ -298,7 +298,7 @@ def _sanitise(*args): values = value.split(' ') for v in values: ## these can be handled separately, without _fix_unsafe(), - ## because they are only allowed if the pass the regex + ## because they are only allowed if they pass the regex if (flag in none_options) and (v is None): continue @@ -332,8 +332,11 @@ def _sanitise(*args): if flag in ['--encrypt', '--encrypt-files', '--decrypt', '--decrypt-files', '--import', '--verify']: - if _util._is_file(val): checked += (val + " ") - else: log.debug("%s not file: %s" % (flag, val)) + if _util._is_file(val) or \ + (flag == '--verify' and val == '-'): + checked += (val + " ") + else: + log.debug("%s not file: %s" % (flag, val)) elif flag in ['--cipher-algo', '--personal-cipher-prefs', '--personal-cipher-preferences']: @@ -372,7 +375,8 @@ def _sanitise(*args): groups[last] = str(filo.pop()) ## accept the read-from-stdin arg: if len(filo) >= 1 and filo[len(filo)-1] == '-': - groups[last] += str(' - \'\'') ## gross hack + groups[last] += str(' - ') ## gross hack + filo.pop() else: groups[last] = str() while len(filo) > 1 and not is_flag(filo[len(filo)-1]): diff --git a/gnupg/gnupg.py b/gnupg/gnupg.py index 489e652..7d6fc30 100644 --- a/gnupg/gnupg.py +++ b/gnupg/gnupg.py @@ -308,7 +308,7 @@ class GPG(GPGBase): sig_fh = None try: sig_fh = open(sig_file) - args = ["--verify %s - " % sig_fh.name] + args = ["--verify %s -" % sig_fh.name] proc = self._open_subprocess(args) writer = _util._threaded_copy_data(file, proc.stdin) self._collect_output(proc, result, stdin=proc.stdin) From fa8216e1f7907836f8bdac8ce8a1ba8728509f7b Mon Sep 17 00:00:00 2001 From: Thomas Tanner Date: Tue, 12 Nov 2013 12:14:56 +0100 Subject: [PATCH 09/56] bugfix: find default_key in sign --- gnupg/gnupg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnupg/gnupg.py b/gnupg/gnupg.py index 0605f68..27c48bb 100644 --- a/gnupg/gnupg.py +++ b/gnupg/gnupg.py @@ -239,7 +239,7 @@ class GPG(GPGBase): ``$ gpg --with-colons --list-config digestname``. The default, if unspecified, is ``'SHA512'``. """ - if 'default_key' in kwargs.items(): + if 'default_key' in kwargs: log.info("Signing message '%r' with keyid: %s" % (data, kwargs['default_key'])) else: From b0dc01b60883428db877c20e71f5d7d28cb39a71 Mon Sep 17 00:00:00 2001 From: Thomas Tanner Date: Sun, 17 Nov 2013 14:45:22 +0100 Subject: [PATCH 10/56] remove print statements in logger --- gnupg/_logger.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/gnupg/_logger.py b/gnupg/_logger.py index df56b85..29691a4 100644 --- a/gnupg/_logger.py +++ b/gnupg/_logger.py @@ -86,10 +86,8 @@ def create_logger(level=logging.NOTSET): formatr = logging.Formatter(_fmt) handler.setFormatter(formatr) - print("Starting the logger...") else: handler = NullHandler() - print("GnuPG logging disabled...") log = logging.getLogger('gnupg') log.addHandler(handler) From a1c104541cf1dffaacee3d9d664cc6dddcac4688 Mon Sep 17 00:00:00 2001 From: Chris Ward Date: Thu, 21 Nov 2013 15:06:36 +0100 Subject: [PATCH 11/56] py2.6 compatability; fallback to trying ordereddict.OrderedDict if collections.OrderedDict is not available --- gnupg/_parsers.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/gnupg/_parsers.py b/gnupg/_parsers.py index e409afc..caa9260 100644 --- a/gnupg/_parsers.py +++ b/gnupg/_parsers.py @@ -7,12 +7,12 @@ # © 2008-2012 Vinay Sajip # © 2005 Steve Traugott # © 2004 A.M. Kuchling -# +# # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) # any later version. -# +# # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the included LICENSE file for details. @@ -25,7 +25,14 @@ Classes for parsing GnuPG status messages and sanitising commandline options. from __future__ import absolute_import from __future__ import print_function -import collections +try: + # py2.7+ comes with collections.OrderedDict + from collections import OrderedDict +except ImportError: + # py2.6 doesn't; this is an equivalent; + # `pip install ordereddict` + from ordereddict import OrderedDict + import re from . import _util @@ -1008,7 +1015,7 @@ class ImportResult(object): _fields = '''count no_user_id imported imported_rsa unchanged n_uids n_subk n_sigs n_revoc sec_read sec_imported sec_dups not_imported'''.split() - _counts = collections.OrderedDict( + _counts = OrderedDict( zip(_fields, [int(0) for x in range(len(_fields))]) ) #: A list of strings containing the fingerprints of the GnuPG keyIDs From 15f83fe508cc75022fc7d80b134a14e37e15f615 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 2 Dec 2013 07:45:59 +0000 Subject: [PATCH 12/56] Fix GPGBase._homedir_setting docstring parameter name. --- gnupg/_meta.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gnupg/_meta.py b/gnupg/_meta.py index 57a0dfb..14ef62c 100644 --- a/gnupg/_meta.py +++ b/gnupg/_meta.py @@ -321,11 +321,11 @@ class GPGBase(object): created. Lastly, the ``direcory`` will be checked that the EUID has read and write permissions for it. - :param str homedir: A relative or absolute path to the directory to use - for storing/accessing GnuPG's files, including + :param str directory: A relative or absolute path to the directory to + use for storing/accessing GnuPG's files, including keyrings and the trustdb. :raises: :exc:`RuntimeError` if unable to find a suitable directory to - use. + use. """ if not directory: log.debug("GPGBase._homedir_setter(): Using default homedir: '%s'" From 33c905ed0f2dbebbac885994d4d90002196cfa0c Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 2 Dec 2013 07:42:07 +0000 Subject: [PATCH 13/56] Fix whitespace indentation levels in _util.InheritableProperty class. --- gnupg/_util.py | 59 +++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/gnupg/_util.py b/gnupg/_util.py index dd80632..c8a1a7b 100644 --- a/gnupg/_util.py +++ b/gnupg/_util.py @@ -529,39 +529,40 @@ def _write_passphrase(stream, passphrase, encoding): class InheritableProperty(object): - """Based on the emulation of PyProperty_Type() in Objects/descrobject.c""" + """Based on the emulation of PyProperty_Type() in Objects/descrobject.c""" - def __init__(self, fget=None, fset=None, fdel=None, doc=None): - self.fget = fget - self.fset = fset - self.fdel = fdel - self.__doc__ = doc + def __init__(self, fget=None, fset=None, fdel=None, doc=None): + self.fget = fget + self.fset = fset + self.fdel = fdel + self.__doc__ = doc - def __get__(self, obj, objtype=None): - if obj is None: - return self - if self.fget is None: - raise AttributeError("unreadable attribute") - if self.fget.__name__ == '' or not self.fget.__name__: - return self.fget(obj) - else: - return getattr(obj, self.fget.__name__)() + def __get__(self, obj, objtype=None): + if obj is None: + return self + if self.fget is None: + raise AttributeError("unreadable attribute") + if self.fget.__name__ == '' or not self.fget.__name__: + return self.fget(obj) + else: + return getattr(obj, self.fget.__name__)() - def __set__(self, obj, value): - if self.fset is None: - raise AttributeError("can't set attribute") - if self.fset.__name__ == '' or not self.fset.__name__: - self.fset(obj, value) - else: - getattr(obj, self.fset.__name__)(value) + def __set__(self, obj, value): + if self.fset is None: + raise AttributeError("can't set attribute") + if self.fset.__name__ == '' or not self.fset.__name__: + self.fset(obj, value) + else: + getattr(obj, self.fset.__name__)(value) + + def __delete__(self, obj): + if self.fdel is None: + raise AttributeError("can't delete attribute") + if self.fdel.__name__ == '' or not self.fdel.__name__: + self.fdel(obj) + else: + getattr(obj, self.fdel.__name__)() - def __delete__(self, obj): - if self.fdel is None: - raise AttributeError("can't delete attribute") - if self.fdel.__name__ == '' or not self.fdel.__name__: - self.fdel(obj) - else: - getattr(obj, self.fdel.__name__)() class Storage(dict): """A dictionary where keys are stored as class attributes. From 83f37272b31b25a1c87fc6a2dd096a9d09eca421 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 00:06:34 +0000 Subject: [PATCH 14/56] Pin psutil==1.2.1 and add a SHA-256 hash for peep. See https://pypi.python.org/pypi/peep/ for a pip wrapper which pins packages by hash digest in a requirements.txt file. --- requirements.txt | 61 +++--------------------------------------------- 1 file changed, 3 insertions(+), 58 deletions(-) diff --git a/requirements.txt b/requirements.txt index fc171a9..2b610ba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,58 +1,3 @@ -# -# python-gnupg/requirements.txt -# ----------------------------- -# Pip requirements.txt file. This file is also parsed for distribute to use in -# setup.py. -#_____________________________________________________________________________ -# This file is part of python-gnupg, a Python interface to GnuPG. -# Copyright © 2013 Isis Lovecruft, 0xA3ADB67A2CDB8B35 -# © 2013 Andrej B. -# © 2013 LEAP Encryption Access Project -# © 2008-2012 Vinay Sajip -# © 2005 Steve Traugott -# © 2004 A.M. Kuchling -# -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the Free -# Software Foundation, either version 3 of the License, or (at your option) -# any later version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the included LICENSE file for details. -#______________________________________________________________________________ -# -# Force pip upgrade due to security vulnerabilities. -# -# This has actually has little to do with installing python-gnupg, since -# older versions of pip would install everything just fine. except that, in -# my opinion, using GnuPG for privacy is silly when the installation of -# python-gnupg with an older version of pip is trivially exploitable through -# a MITM attack. see https://github.com/pypa/pip/pull/791 -# -# Also, note that SSL package delivery is *not* entirely fixed yet. See -# https://github.com/TheTorProject/ooni-backend/pull/1#discussion_r4084881 -# -#pip>=1.3.1 -# -# NOTE: setuptools is currently (as of 27 May 2013) being merged back into its -# parent project, distribute. By using the included distribute_setup.py -# script, we make sure that we have a recent version of setuptools/distribute, -# which is the *only* Python packaging framework compatible at this point with -# both Python>=2.4 and Python3.x. -# -# A new version of distribute is necessary due to the merging of setuptools -# back into its parent project, distribute. Also, the only way to package for -# both Python 2 and 3 is to use distribute. -# -#distribute>=0.6.45 -# -# Sphinx is only necessary for building documentation, so it is added in -# setup.py under extras_require['docs']. -# -# If you want to build the documentation, uncomment this line: -#Sphinx>=1.1 -# -# And, this one is actually used in the gnupg module code: -# -psutil>=0.5.1 +# sha256: UI5KRMglOjhqD4bZyb1KG0y7L5TojUmhnBUTZTymbEU +psutil==1.2.1 + From 96c3ee97f94dccd9955f88cec3678f32c242b727 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 10:54:13 +0000 Subject: [PATCH 15/56] Fix Sphinx build failing due to missing psutil import. --- docs/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/conf.py b/docs/conf.py index 049ed5a..248f450 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -12,6 +12,7 @@ # serve to show the default. import sys, os +import psutil # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the From aa3ff37a2b84b763800a61153173891dd45b402f Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 11:20:32 +0000 Subject: [PATCH 16/56] Add Makefile commands for creating zipped doc builds. --- Makefile | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index bb05749..9dfa649 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,11 @@ SHELL=/bin/sh TESTDIR=./gnupg/test TESTHANDLE=$(TESTDIR)/test_gnupg.py FILES=$(SHELL find ./gnupg/ -name "*.py" -printf "%p,") +PKG_NAME=python-gnupg +DOC_DIR=docs +DOC_BUILD_DIR:=$(DOC_DIR)/_build +DOC_HTML_DIR:=$(DOC_BUILD_DIR)/html +DOC_BUILD_ZIP:=$(PKG_NAME)-docs.zip .PHONY=all all: uninstall install test @@ -73,11 +78,15 @@ py3k-uninstall: uninstall reinstall: uninstall install py3k-reinstall: py3k-uninstall py3k-install -cleandocs: - sphinx-apidoc -F -A "Isis Agora Lovecruft" -H "python-gnupg" \ - -o docs gnupg/ tests/ +docs-clean: + -rm -rf $(DOC_BUILD_DIR) -docs: - cd docs && \ - make clean && \ - make html +docs-completely-new: + sphinx-apidoc -F -A "Isis Agora Lovecruft" -H "python-gnupg" -o $(DOC_DIR) gnupg/ tests/ + +docs-html: + cd $(DOC_DIR) && make clean && make html + +docs-zipfile: docs-html + cd $(DOC_HTML_DIR) && { find . -name '*' | zip -@ -v ../$(DOC_BUILD_ZIP) ;}; + @echo "Built documentation in $(DOC_BUILD_DIR)/$(DOC_BUILD_ZIP)" From ce30d9a1aef5fe6a595e643f74985d203b647a08 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 11:21:37 +0000 Subject: [PATCH 17/56] Update setup.py to use dependency links from requirements.txt. --- setup.py | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 5def919..3b4154d 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,10 @@ from __future__ import absolute_import from __future__ import print_function import setuptools +import os import versioneer + + versioneer.versionfile_source = 'gnupg/_version.py' versioneer.versionfile_build = 'gnupg/_version.py' versioneer.tag_prefix = '' @@ -34,6 +37,44 @@ __contact__ = 'isis@patternsinthevoid.net' __url__ = 'https://github.com/isislovecruft/python-gnupg' +def get_requirements(): + """Extract the list of requirements from our requirements.txt. + + :rtype: 2-tuple + :returns: Two lists, the first is a list of requirements in the form of + pkgname==version. The second is a list of URIs or VCS checkout strings + which specify the dependency links for obtaining a copy of the + requirement. + """ + requirements_file = os.path.join(os.getcwd(), 'requirements.txt') + requirements = [] + links=[] + try: + with open(requirements_file) as reqfile: + for line in reqfile.readlines(): + line = line.strip() + if line.startswith('#'): + continue + elif line.startswith( + ('https://', 'git://', 'hg://', 'svn://')): + links.append(line) + else: + requirements.append(line) + + except (IOError, OSError) as error: + print(error) + + return requirements, links + + +requires, deplinks = get_requirements() +print('Found requirements:') +[print('\t%s' % name) for name in requires] + +print('Found dependency links:') +[print('\t%s' % uri) for uri in deplinks] + + setuptools.setup( name = "gnupg", description="A Python wrapper for GnuPG", @@ -62,7 +103,8 @@ greater. scripts=['versioneer.py'], test_suite='gnupg.test.test_gnupg', - install_requires=['psutil>=0.5.1'], + install_requires=requires, + dependency_links=deplinks, extras_require={'docs': ["Sphinx>=1.1", "repoze.sphinx"]}, platforms="Linux, BSD, OSX, Windows", From b0323997059233f4bbbe0b0b707df963a2d1b84e Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 11:22:10 +0000 Subject: [PATCH 18/56] Add additional setup.cfg [upload_docs] parameters. --- setup.cfg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.cfg b/setup.cfg index a906e65..fb18565 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,7 @@ [upload_docs] upload-dir = docs/_build/html +show-response = true +verbose = true [upload] sign = True From dd9adb8f7a4bee2aff5e00f5e810a37f3dcb4d14 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 11:37:32 +0000 Subject: [PATCH 19/56] Add upload_all setup.cfg alias. --- setup.cfg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.cfg b/setup.cfg index fb18565..530e4e1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,3 +7,6 @@ verbose = true sign = True identity = 0xa3adb67a2cdb8b35 +[aliases] +upload_all = 'sdist bdist_egg bdist_wheel upload' + From 6cf7e0fe61b8d893fff503ad4b51487c033f6bb4 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 14:13:53 +0000 Subject: [PATCH 20/56] Revert "remove docs/_static folder." This reverts commit e5351cfe957cfacab60dded18bc825aa5a4a685b. As mentioned on the issue tracker, [0] some of the CSS from docs/_static was still required to make the layout of the Sphinx generated output have the correct layout. I reverted the last commit, which removed the whole docs/_static directory, and then removed all the JavaScript and the unneeded CSS. Hopefully, this is okay with Debian. :) [0]: https://github.com/isislovecruft/python-gnupg/pull/27#issuecomment-29712108 --- .gitignore | 1 - docs/_static/agogo.css | 327 ++++++++++++++++++++++++++++++++++++++ docs/_static/pygments.css | 69 ++++++++ 3 files changed, 396 insertions(+), 1 deletion(-) create mode 100644 docs/_static/agogo.css create mode 100644 docs/_static/pygments.css diff --git a/.gitignore b/.gitignore index d39197d..a8bfc2b 100644 --- a/.gitignore +++ b/.gitignore @@ -86,4 +86,3 @@ MANIFEST # sphinx default build docs/_build -docs/_static diff --git a/docs/_static/agogo.css b/docs/_static/agogo.css new file mode 100644 index 0000000..9d6a4af --- /dev/null +++ b/docs/_static/agogo.css @@ -0,0 +1,327 @@ +* { + margin: 0px; + padding: 0px; +} + +body { + font-family: "Verdana", Arial, sans-serif; + line-height: 1.4em; + font-size: 14px; + color: black; + background-color: #eeeeec; +} + + +/* Page layout */ + +div.header, div.content, div.footer { + width: 70em; + margin-left: auto; + margin-right: auto; +} + +div.header-wrapper { + background: url(bgtop.png) top left repeat-x; + border-bottom: 3px solid #2e3436; +} + + +/* Default body styles */ +a { + text-decoration: none; + color: #ce5c00; +} + +.clearer { + clear: both; +} + +.left { + float: left; +} + +.right { + float: right; +} + +h1, h2, h3, h4 { + font-family: "Georgia", "Times New Roman", serif; + font-weight: normal; + color: #3465a4; + margin-bottom: .8em; +} + +h1 { + color: #204a87; +} + +h2 { + padding-bottom: .5em; + border-bottom: 1px solid #3465a4; +} + +a.headerlink { + visibility: hidden; + color: #dddddd; + padding-left: .3em; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink { + visibility: visible; +} + + + +/* Header */ + +div.header { + padding-top: 10px; + padding-bottom: 10px; +} + +div.header h1 { + font-family: "Georgia", "Times New Roman", serif; + font-weight: normal; + font-size: 160%; + letter-spacing: .08em; +} + +div.header h1 a { + color: white; +} + +div.header div.rel { + margin-top: 1em; +} + +div.header div.rel a { + color: #fcaf3e; + letter-spacing: .1em; + text-transform: uppercase; +} + + +/* Content */ +div.content-wrapper { + background-color: white; + padding-top: 20px; + padding-bottom: 20px; +} + +div.document { + width: 50em; + float: left; +} + +div.body { + padding-right: 2em; + text-align: justify; +} + +div.document ul { + margin-left: 1.2em; + list-style-type: square; +} + +div.document dd { + margin-left: 1.2em; + margin-top: .4em; + margin-bottom: 1em; +} + +div.document .section { + margin-top: 1.7em; +} +div.document .section:first-child { + margin-top: 0px; +} + +div.document div.highlight { + padding: 3px; + background-color: #eeeeec; + border-top: 2px solid #dddddd; + border-bottom: 2px solid #dddddd; + margin-top: .8em; + margin-bottom: .8em; +} + +div.document h2 { + margin-top: .7em; +} + +div.document p { + margin-bottom: .5em; +} + +div.document li.toctree-l1 { + margin-bottom: 1em; +} + +div.document .descname { + font-weight: bold; +} + +div.document .docutils.literal { + background-color: #eeeeec; + padding: 1px; +} + +div.document .docutils.xref.literal { + background-color: transparent; + padding: 0px; +} + + +/* Sidebar */ + +div.sidebar { + width: 20em; + float: right; + font-size: .9em; +} + +div.sidebar h3 { + color: #2e3436; + text-transform: uppercase; + font-size: 130%; + letter-spacing: .1em; +} + +div.sidebar ul { + list-style-type: none; +} + +div.sidebar li.toctree-l1 a { + display: block; + padding: 1px; + border: 1px solid #dddddd; + background-color: #eeeeec; + margin-bottom: .4em; + padding-left: 3px; + color: #2e3436; +} + +div.sidebar li.toctree-l2 a { + background-color: transparent; + border: none; + border-bottom: 1px solid #dddddd; +} + +div.sidebar li.toctree-l2:last-child a { + border-bottom: none; +} + +div.sidebar li.toctree-l1.current a { + border-right: 5px solid #fcaf3e; +} + +div.sidebar li.toctree-l1.current li.toctree-l2 a { + border-right: none; +} + + +/* Footer */ + +div.footer-wrapper { + background: url(bgfooter.png) top left repeat-x; + border-top: 4px solid #babdb6; + padding-top: 10px; + padding-bottom: 10px; + min-height: 80px; +} + +div.footer, div.footer a { + color: #888a85; +} + +div.footer .right { + text-align: right; +} + +div.footer .left { + text-transform: uppercase; +} + + +/* Styles copied form basic theme */ + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li div.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable dl, table.indextable dd { + margin-top: 0; + margin-bottom: 0; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} diff --git a/docs/_static/pygments.css b/docs/_static/pygments.css new file mode 100644 index 0000000..45bae6e --- /dev/null +++ b/docs/_static/pygments.css @@ -0,0 +1,69 @@ +.hll { background-color: #ffffcc } +.c { color: #8f5902; font-style: italic } /* Comment */ +.err { color: #a40000; border: 1px solid #ef2929 } /* Error */ +.g { color: #000000 } /* Generic */ +.k { color: #204a87; font-weight: bold } /* Keyword */ +.l { color: #000000 } /* Literal */ +.n { color: #000000 } /* Name */ +.o { color: #ce5c00; font-weight: bold } /* Operator */ +.x { color: #000000 } /* Other */ +.p { color: #000000; font-weight: bold } /* Punctuation */ +.cm { color: #8f5902; font-style: italic } /* Comment.Multiline */ +.cp { color: #8f5902; font-style: italic } /* Comment.Preproc */ +.c1 { color: #8f5902; font-style: italic } /* Comment.Single */ +.cs { color: #8f5902; font-style: italic } /* Comment.Special */ +.gd { color: #a40000 } /* Generic.Deleted */ +.ge { color: #000000; font-style: italic } /* Generic.Emph */ +.gr { color: #ef2929 } /* Generic.Error */ +.gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.gi { color: #00A000 } /* Generic.Inserted */ +.go { color: #000000; font-style: italic } /* Generic.Output */ +.gp { color: #8f5902 } /* Generic.Prompt */ +.gs { color: #000000; font-weight: bold } /* Generic.Strong */ +.gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.gt { color: #a40000; font-weight: bold } /* Generic.Traceback */ +.kc { color: #204a87; font-weight: bold } /* Keyword.Constant */ +.kd { color: #204a87; font-weight: bold } /* Keyword.Declaration */ +.kn { color: #204a87; font-weight: bold } /* Keyword.Namespace */ +.kp { color: #204a87; font-weight: bold } /* Keyword.Pseudo */ +.kr { color: #204a87; font-weight: bold } /* Keyword.Reserved */ +.kt { color: #204a87; font-weight: bold } /* Keyword.Type */ +.ld { color: #000000 } /* Literal.Date */ +.m { color: #0000cf; font-weight: bold } /* Literal.Number */ +.s { color: #4e9a06 } /* Literal.String */ +.na { color: #c4a000 } /* Name.Attribute */ +.nb { color: #204a87 } /* Name.Builtin */ +.nc { color: #000000 } /* Name.Class */ +.no { color: #000000 } /* Name.Constant */ +.nd { color: #5c35cc; font-weight: bold } /* Name.Decorator */ +.ni { color: #ce5c00 } /* Name.Entity */ +.ne { color: #cc0000; font-weight: bold } /* Name.Exception */ +.nf { color: #000000 } /* Name.Function */ +.nl { color: #f57900 } /* Name.Label */ +.nn { color: #000000 } /* Name.Namespace */ +.nx { color: #000000 } /* Name.Other */ +.py { color: #000000 } /* Name.Property */ +.nt { color: #204a87; font-weight: bold } /* Name.Tag */ +.nv { color: #000000 } /* Name.Variable */ +.ow { color: #204a87; font-weight: bold } /* Operator.Word */ +.w { color: #f8f8f8; text-decoration: underline } /* Text.Whitespace */ +.mf { color: #0000cf; font-weight: bold } /* Literal.Number.Float */ +.mh { color: #0000cf; font-weight: bold } /* Literal.Number.Hex */ +.mi { color: #0000cf; font-weight: bold } /* Literal.Number.Integer */ +.mo { color: #0000cf; font-weight: bold } /* Literal.Number.Oct */ +.sb { color: #4e9a06 } /* Literal.String.Backtick */ +.sc { color: #4e9a06 } /* Literal.String.Char */ +.sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */ +.s2 { color: #4e9a06 } /* Literal.String.Double */ +.se { color: #4e9a06 } /* Literal.String.Escape */ +.sh { color: #4e9a06 } /* Literal.String.Heredoc */ +.si { color: #4e9a06 } /* Literal.String.Interpol */ +.sx { color: #4e9a06 } /* Literal.String.Other */ +.sr { color: #4e9a06 } /* Literal.String.Regex */ +.s1 { color: #4e9a06 } /* Literal.String.Single */ +.ss { color: #4e9a06 } /* Literal.String.Symbol */ +.bp { color: #3465a4 } /* Name.Builtin.Pseudo */ +.vc { color: #000000 } /* Name.Variable.Class */ +.vg { color: #000000 } /* Name.Variable.Global */ +.vi { color: #000000 } /* Name.Variable.Instance */ +.il { color: #0000cf; font-weight: bold } /* Literal.Number.Integer.Long */ From 0e6171a4af6f85a1cb6fb6d291f2caa194cbe43a Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 21:47:39 +0000 Subject: [PATCH 21/56] Move DETAILS.html into _static/ so that the documentation can link to it. --- docs/{ => _static}/DETAILS.html | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/{ => _static}/DETAILS.html (100%) diff --git a/docs/DETAILS.html b/docs/_static/DETAILS.html similarity index 100% rename from docs/DETAILS.html rename to docs/_static/DETAILS.html From 7d7dec5d9ffbeca6815e8b8421f68a995b4f9aa1 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 21:49:46 +0000 Subject: [PATCH 22/56] Make doc html head font easier to read. --- docs/_static/agogo.css | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/_static/agogo.css b/docs/_static/agogo.css index 9d6a4af..2bdc26f 100644 --- a/docs/_static/agogo.css +++ b/docs/_static/agogo.css @@ -25,6 +25,16 @@ div.header-wrapper { border-bottom: 3px solid #2e3436; } +div.headertitle a { + font-family: "Georgia", "Times New Roman", serif; + font-size: 2em; + color: rgb(252, 175, 62); + font-weight: normal; +} + +h1 { + color: #204a87; +} /* Default body styles */ a { From 638d6395586b897e86f9d3ffba038d34ad89af17 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 21:51:09 +0000 Subject: [PATCH 23/56] Add options to Sphinx conf.py for -Wall and rebuild environment. --- docs/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Makefile b/docs/Makefile index 7159d3e..2d43a93 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -2,7 +2,7 @@ # # You can set these variables from the command line. -SPHINXOPTS = +SPHINXOPTS = -E -n SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build From 66c3a1b693a5c921993d3cf8711a1cdf4a800f73 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 21:53:12 +0000 Subject: [PATCH 24/56] Update Sphinx version requirement. --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 248f450..67c3333 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -29,7 +29,7 @@ autoclass_content = 'both' # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. -needs_sphinx = '1.0' +needs_sphinx = '1.1' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. From 0f611c3c133515775dc6225c1858a38e95de9971 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 21:53:54 +0000 Subject: [PATCH 25/56] Add intersphinx mapping to docs.python.org. --- docs/conf.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 67c3333..17a4c08 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -33,7 +33,12 @@ needs_sphinx = '1.1' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode'] +extensions = ['sphinx.ext.autodoc', + 'sphinx.ext.viewcode', + 'sphinx.ext.intersphinx', + 'sphinx.ext.doctest', + 'sphinxcontrib.fulltoc', + ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_static'] @@ -95,6 +100,8 @@ pygments_style = 'monokai' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {'http://docs.python.org/': None} # -- Options for HTML output --------------------------------------------------- From 91ef91186213ad2b0e66991cfaf71e35e34eb494 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 21:54:29 +0000 Subject: [PATCH 26/56] Specify Sphinx source encoding explicitly. --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 17a4c08..84287a9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -47,7 +47,7 @@ templates_path = ['_static'] source_suffix = '.rst' # The encoding of source files. -#source_encoding = 'utf-8-sig' +source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' From 15d5635ea44fc0f1e73a96d234bb675fd0b8cd8c Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 21:55:08 +0000 Subject: [PATCH 27/56] Disable Sphinx prefixing with full module names. --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 84287a9..4986e0b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -88,7 +88,7 @@ add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +add_module_names = False # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. From 8dda32636f46415f5578ce7d106d6d27a103ceac Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 21:55:57 +0000 Subject: [PATCH 28/56] Add reference Sphinx directives for module sections in docs. --- docs/gnupg.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/gnupg.rst b/docs/gnupg.rst index c216846..6ff0a2e 100644 --- a/docs/gnupg.rst +++ b/docs/gnupg.rst @@ -17,6 +17,7 @@ do: :private-members: :show-inheritance: +.. _meta: meta module ----------- @@ -32,6 +33,7 @@ doing some serious hacking. :private-members: :show-inheritance: +.. _parsers: parsers module -------------- @@ -52,6 +54,8 @@ source_, which has been included here_ as well. :show-inheritance: +.. _util: + util module ----------- From 9a4f9c2f9a9517a8a41f639ad46fba455ff90b67 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 21:56:59 +0000 Subject: [PATCH 29/56] Don't document special attributes in _meta twice. --- docs/gnupg.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/gnupg.rst b/docs/gnupg.rst index 6ff0a2e..dd96065 100644 --- a/docs/gnupg.rst +++ b/docs/gnupg.rst @@ -29,8 +29,10 @@ doing some serious hacking. .. automodule:: gnupg._meta :members: - :undoc-members: :private-members: + :special-members: + :exclude-members: _agent_proc, __module__, __dict__, _decode_errors, init, + __weakref__, _result_map, __metaclass__ :show-inheritance: .. _parsers: From 87b8c97a344e5b7e265267ecca761a3116555a15 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 21:58:39 +0000 Subject: [PATCH 30/56] Fix a couple refs pointing to the wrong classes in docs/gnupg.rst. --- docs/gnupg.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/gnupg.rst b/docs/gnupg.rst index dd96065..5e0b0a8 100644 --- a/docs/gnupg.rst +++ b/docs/gnupg.rst @@ -43,10 +43,11 @@ parsers module These are classes for parsing both user inputs and status file descriptor flags from GnuPG's output. The latter are used in order to determine what our GnuPG process is doing and retrieve information about its operations, which -are stored in corresponding classes in :attr:`gnupg.GPG._result_dict`. Some -status flags aren't handled yet -- infomation on *all* of the flags (well, at -least the documented ones…) can be found in the docs/DETAILS file in GnuPG's -source_, which has been included here_ as well. +are stored in corresponding classes in +:attr:`~gnupg._meta.GPGBase._result_map`. Some status flags aren't handled yet +-- information on *all* of the flags (well, at least the documented ones…) can +be found in the :file:`docs/DETAILS` file in GnuPG's source_, which has been +included here_ as well. .. automodule:: gnupg._parsers @@ -81,10 +82,9 @@ by Steve Traugott, which in turn is a modification of the pycrypto GnuPG interface written by A.M. Kuchling. This version is patched to sanitize untrusted inputs, due to the necessity of -executing :class:`subprocess.Popen([...], shell=True)` in order to communicate -with GnuPG. Several speed improvements were also made based on code profiling, -and the API has been cleaned up to support an easier, more Pythonic, -interaction. +executing ``subprocess.Popen([...], shell=True)`` in order to communicate with +GnuPG. Several speed improvements were also made based on code profiling, and +the API has been cleaned up to support an easier, more Pythonic, interaction. Previous Authors' Documentation @@ -128,4 +128,4 @@ Vinay Sajip's documentation: .. _GnuPG: http://gnupg.org .. _python-gnupg: https://code.google.com/p/python-gnupg/ .. _source: http://http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=shortlog;h=refs/heads/master -.. _here: ./DETAILS.html +.. _here: ./_static/DETAILS.html From cfff8aaba86bce43d9bfe198c5640a57d988f0b0 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 22:00:09 +0000 Subject: [PATCH 31/56] Fix blockquotes of previous documentation in gnupg.rst. --- docs/gnupg.rst | 58 +++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/docs/gnupg.rst b/docs/gnupg.rst index 5e0b0a8..fc8a479 100644 --- a/docs/gnupg.rst +++ b/docs/gnupg.rst @@ -91,38 +91,38 @@ Previous Authors' Documentation ------------------------------- Steve Traugott's documentation: - - | Portions of this module are derived from A.M. Kuchling's well-designed - | GPG.py, using Richard Jones' updated version 1.3, which can be found in - | the pycrypto CVS repository on Sourceforge: - | - | http://pycrypto.cvs.sourceforge.net/viewvc/pycrypto/gpg/GPG.py - | - | This module is *not* forward-compatible with amk's; some of the old - | interface has changed. For instance, since I've added decrypt - | functionality, I elected to initialize with a 'gpghome' argument instead - | of 'keyring', so that gpg can find both the public and secret keyrings. - | I've also altered some of the returned objects in order for the caller to - | not have to know as much about the internals of the result classes. - | - | While the rest of ISconf is released under the GPL, I am releasing this - | single file under the same terms that A.M. Kuchling used for pycrypto. - | - | Steve Traugott, stevegt@terraluna.org - | Thu Jun 23 21:27:20 PDT 2005 + | + | Portions of this module are derived from A.M. Kuchling's well-designed + | GPG.py, using Richard Jones' updated version 1.3, which can be found in + | the pycrypto CVS repository on Sourceforge: + | + | http://pycrypto.cvs.sourceforge.net/viewvc/pycrypto/gpg/GPG.py + | + | This module is *not* forward-compatible with amk's; some of the old + | interface has changed. For instance, since I've added decrypt + | functionality, I elected to initialize with a 'gpghome' argument instead + | of 'keyring', so that gpg can find both the public and secret keyrings. + | I've also altered some of the returned objects in order for the caller to + | not have to know as much about the internals of the result classes. + | + | While the rest of ISconf is released under the GPL, I am releasing this + | single file under the same terms that A.M. Kuchling used for pycrypto. + | + | Steve Traugott, stevegt@terraluna.org + | Thu Jun 23 21:27:20 PDT 2005 Vinay Sajip's documentation: - - | This version of the module has been modified from Steve Traugott's version - | (see http://trac.t7a.org/isconf/browser/trunk/lib/python/isconf/GPG.py) by - | Vinay Sajip to make use of the subprocess module (Steve's version uses - | os.fork() and so does not work on Windows). Renamed to gnupg.py to avoid - | confusion with the previous versions. - | - | A unittest harness (test_gnupg.py) has also been added. - | - | Modifications Copyright (C) 2008-2012 Vinay Sajip. All rights reserved. + | + | This version of the module has been modified from Steve Traugott's version + | (see http://trac.t7a.org/isconf/browser/trunk/lib/python/isconf/GPG.py) by + | Vinay Sajip to make use of the subprocess module (Steve's version uses + | os.fork() and so does not work on Windows). Renamed to gnupg.py to avoid + | confusion with the previous versions. + | + | A unittest harness (test_gnupg.py) has also been added. + | + | Modifications Copyright (C) 2008-2012 Vinay Sajip. All rights reserved. .. _GnuPG: http://gnupg.org From f38caae84d3016406db517cade70b693100446f6 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 22:21:36 +0000 Subject: [PATCH 32/56] Remove duplicate headings from module docstrings. --- gnupg/_logger.py | 5 +---- gnupg/_meta.py | 6 ++---- gnupg/_parsers.py | 5 ++--- gnupg/_trust.py | 4 +--- gnupg/_util.py | 5 +---- 5 files changed, 7 insertions(+), 18 deletions(-) diff --git a/gnupg/_logger.py b/gnupg/_logger.py index df56b85..cf2e081 100644 --- a/gnupg/_logger.py +++ b/gnupg/_logger.py @@ -17,10 +17,7 @@ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the included LICENSE file for details. -'''log.py ----------- -Logging module for python-gnupg. -''' +'''Logging module for python-gnupg.''' from __future__ import absolute_import from __future__ import print_function diff --git a/gnupg/_meta.py b/gnupg/_meta.py index 0659968..46956fb 100644 --- a/gnupg/_meta.py +++ b/gnupg/_meta.py @@ -17,10 +17,8 @@ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the included LICENSE file for details. -'''meta.py ----------- -Meta and base classes for hiding internal functions, and controlling attribute -creation and handling. +'''Meta and base classes for hiding internal functions, and controlling +attribute creation and handling. ''' from __future__ import absolute_import diff --git a/gnupg/_parsers.py b/gnupg/_parsers.py index 1c99c0d..9c09002 100644 --- a/gnupg/_parsers.py +++ b/gnupg/_parsers.py @@ -17,9 +17,8 @@ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the included LICENSE file for details. -'''parsers.py -------------- -Classes for parsing GnuPG status messages and sanitising commandline options. +'''Classes for parsing GnuPG status messages and sanitising commandline +options. ''' from __future__ import absolute_import diff --git a/gnupg/_trust.py b/gnupg/_trust.py index 1f5b266..bb28e34 100644 --- a/gnupg/_trust.py +++ b/gnupg/_trust.py @@ -17,9 +17,7 @@ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the included LICENSE file for details. -'''trust.py ------------ -Functions for handling trustdb and trust calculations. +'''Functions for handling trustdb and trust calculations. The functions within this module take an instance of :class:`gnupg.GPGBase` or a suitable subclass as their first argument. diff --git a/gnupg/_util.py b/gnupg/_util.py index f5b7ff3..eb76190 100644 --- a/gnupg/_util.py +++ b/gnupg/_util.py @@ -17,10 +17,7 @@ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the included LICENSE file for details. -'''util.py ----------- -Extra utilities for python-gnupg. -''' +'''Extra utilities for python-gnupg.''' from __future__ import absolute_import from datetime import datetime From 3c155e15111bed63757b5ba0c4829e4fc2474538 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 22:22:10 +0000 Subject: [PATCH 33/56] Fix the rst table of logging level names and values. --- gnupg/_logger.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/gnupg/_logger.py b/gnupg/_logger.py index cf2e081..1f807d5 100644 --- a/gnupg/_logger.py +++ b/gnupg/_logger.py @@ -48,17 +48,23 @@ def status(self, message, *args, **kwargs): def create_logger(level=logging.NOTSET): """Create a logger for python-gnupg at a specific message level. - :type level: int or str - :param level: A string or an integer for the lowest level to log. - Available levels: - int str description - 0 NOTSET Disable all logging. - 9 GNUPG Log GnuPG's internal status messages. - 10 DEBUG Log module level debuging messages. - 20 INFO Normal user-level messages. - 30 WARN Warning messages. - 40 ERROR Error messages and tracebacks. - 50 CRITICAL Unhandled exceptions and tracebacks. + :type level: :obj:`int` or :obj:`str` + :param level: A string or an integer for the lowest level to include in + logs. + + **Available levels:** + + ==== ======== ======================================== + int str description + ==== ======== ======================================== + 0 NOTSET Disable all logging. + 9 GNUPG Log GnuPG's internal status messages. + 10 DEBUG Log module level debuging messages. + 20 INFO Normal user-level messages. + 30 WARN Warning messages. + 40 ERROR Error messages and tracebacks. + 50 CRITICAL Unhandled exceptions and tracebacks. + ==== ======== ======================================== """ _test = os.path.join(os.path.join(os.getcwd(), 'gnupg'), 'test') _now = datetime.now().strftime("%Y-%m-%d_%H%M%S") From 9d75e0f01b1d248ef2fd850d49cb71219cf92115 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 22:23:44 +0000 Subject: [PATCH 34/56] Cleanup Sphinx directives in docstrings in _trust.py. --- gnupg/_trust.py | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/gnupg/_trust.py b/gnupg/_trust.py index bb28e34..9f72ac6 100644 --- a/gnupg/_trust.py +++ b/gnupg/_trust.py @@ -42,11 +42,12 @@ def _create_trustdb(cls): def export_ownertrust(cls, trustdb=None): """Export ownertrust to a trustdb file. - If there is already a file named 'trustdb.gpg' in the current GnuPG - homedir, it will be renamed to 'trustdb.gpg.bak'. + If there is already a file named :file:`trustdb.gpg` in the current GnuPG + homedir, it will be renamed to :file:`trustdb.gpg.bak`. :param string trustdb: The path to the trustdb.gpg file. If not given, - defaults to 'trustdb.gpg' in the current GnuPG homedir. + defaults to ``'trustdb.gpg'`` in the current GnuPG + homedir. """ if trustdb is None: trustdb = os.path.join(cls.homedir, 'trustdb.gpg') @@ -63,8 +64,9 @@ def export_ownertrust(cls, trustdb=None): def import_ownertrust(self, trustdb=None): """Import ownertrust from a trustdb file. - :param string trustdb: The path to the trustdb.gpg file. If not given, - defaults to 'trustdb.gpg' in the current GnuPG homedir. + :param str trustdb: The path to the trustdb.gpg file. If not given, + defaults to :file:`trustdb.gpg` in the current GnuPG + homedir. """ if trustdb is None: trustdb = os.path.join(cls.homedir, 'trustdb.gpg') @@ -78,20 +80,21 @@ def fix_trustdb(cls, trustdb=None): GnuPG>=2.0.x has this magical-seeming flag: `--fix-trustdb`. You'd think it would fix the the trustdb. Hah! It doesn't. Here's what it does - instead: + instead:: - (python-gnupg)∃!isisⒶwintermute:(testing/digest-algo)~/code/python-gnupg ∴ gpg2 --fix-trustdb - gpg: You may try to re-create the trustdb using the commands: - gpg: cd ~/.gnupg - gpg: gpg2 --export-ownertrust > otrust.tmp - gpg: rm trustdb.gpg - gpg: gpg2 --import-ownertrust < otrust.tmp - gpg: If that does not work, please consult the manual + (gpg)∃!isisⒶwintermute:(testing/algo)~/code/python-gnupg ∴ gpg2 --fix-trustdb + gpg: You may try to re-create the trustdb using the commands: + gpg: cd ~/.gnupg + gpg: gpg2 --export-ownertrust > otrust.tmp + gpg: rm trustdb.gpg + gpg: gpg2 --import-ownertrust < otrust.tmp + gpg: If that does not work, please consult the manual Brilliant piece of software engineering right there. - :param string trustdb: The path to the trustdb.gpg file. If not given, - defaults to 'trustdb.gpg' in the current GnuPG homedir. + :param str trustdb: The path to the trustdb.gpg file. If not given, + defaults to :file:`trustdb.gpg` in the current GnuPG + homedir. """ if trustdb is None: trustdb = os.path.join(cls.homedir, 'trustdb.gpg') From 4f2dc555a56a3148e0145decbc270a4fe9c8bd7d Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 22:25:16 +0000 Subject: [PATCH 35/56] Cleanup Sphinx directives in _meta.py. --- gnupg/_meta.py | 198 ++++++++++++++++++++++++++++--------------------- 1 file changed, 112 insertions(+), 86 deletions(-) diff --git a/gnupg/_meta.py b/gnupg/_meta.py index 46956fb..6654ac6 100644 --- a/gnupg/_meta.py +++ b/gnupg/_meta.py @@ -26,6 +26,7 @@ from __future__ import absolute_import import atexit import codecs import encodings +import exceptions ## For AOS, the locale module will need to point to a wrapper around the ## java.util.Locale class. ## See https://code.patternsinthevoid.net/?p=android-locale-hack.git @@ -50,6 +51,10 @@ class GPGMeta(type): Detects running gpg-agent processes and the presence of a pinentry program, and disables pinentry so that python-gnupg can write the passphrase to the controlled GnuPG process without killing the agent. + + :attr _agent_proc: If a :program:`gpg-agent` process is currently running + for the effective userid, then **_agent_proc** will be + set to a ``psutil.Process`` for that process. """ def __new__(cls, name, bases, attrs): @@ -67,7 +72,7 @@ class GPGMeta(type): If there is a matching gpg-agent process, set a :class:`psutil.Process` instance containing the gpg-agent process' information to - :attr:`GPG._agent_proc`. + ``cls._agent_proc``. :returns: True if there exists a gpg-agent process running under the same effective user ID as that of this program. Otherwise, @@ -85,9 +90,13 @@ class GPGMeta(type): class GPGBase(object): - """Base class for property storage and to control process - initialisation.""" + """Base class for storing properties and controlling process initialisation. + :const _result_map: A *dict* containing classes from + :mod:`~gnupg._parsers`, used for parsing results + obtained from GnuPG commands. + :const _decode_errors: How to handle encoding errors. + """ __metaclass__ = GPGMeta _decode_errors = 'strict' _result_map = { 'crypt': _parsers.Crypt, @@ -102,7 +111,28 @@ class GPGBase(object): def __init__(self, binary=None, home=None, keyring=None, secring=None, use_agent=False, default_preference_list=None, verbose=False, options=None): + """Create a ``GPGBase``. + This class is used to set up properties for controlling the behaviour + of configuring various options for GnuPG, such as setting GnuPG's + **homedir** , and the paths to its **binary** and **keyring** . + + :const binary: (:obj:`str`) The full path to the GnuPG binary. + + :ivar homedir: (:class:`~gnupg._util.InheritableProperty`) The full + path to the current setting for the GnuPG + ``--homedir``. + + :ivar _generated_keys: (:class:`~gnupg._util.InheritableProperty`) + Controls setting the directory for storing any + keys which are generated with + :meth:`~gnupg.GPG.gen_key`. + + :ivar str keyring: The filename in **homedir** to use as the keyring + file for public keys. + :ivar str secring: The filename in **homedir** to use as the keyring + file for secret keys. + """ self.binary = _util._find_binary(binary) self.homedir = home if home else _util._conf pub = _parsers._fix_unsafe(keyring) if keyring else 'pubring.gpg' @@ -153,15 +183,14 @@ class GPGBase(object): self.__remove_path__('pinentry') def __remove_path__(self, prog=None, at_exit=True): - """Remove a the directories containing a program from the system's - ``$PATH``. If :attr:`GPG.binary` is in a directory being removed, it - is symlinked to './gpg' + """Remove the directories containing a program from the system's + ``$PATH``. If ``GPGBase.binary`` is in a directory being removed, it + is linked to :file:'./gpg' in the current directory. :param str prog: The program to remove from ``$PATH``. - :param bool at_exit: Add the program back into the ``$PATH`` when the Python interpreter exits, and delete any symlinks - to :attr:`GPG.binary` which were created. + to ``GPGBase.binary`` which were created. """ #: A list of ``$PATH`` entries which were removed to disable pinentry. self._removed_path_entries = [] @@ -221,7 +250,7 @@ class GPGBase(object): @staticmethod def update_path(environment, path): - """Add paths to the string at os.environ['PATH']. + """Add paths to the string at ``os.environ['PATH']``. :param str environment: The environment mapping to update. :param list path: A list of strings to update the PATH with. @@ -269,7 +298,7 @@ class GPGBase(object): Note that "original state" does not mean the default preference list for whichever version of GnuPG is being used. It means the - default preference list defined by :attr:`GPGBase._preferences`. + default preference list defined by :attr:`GPGBase._prefs`. Using BZIP2 is avoided due to not interacting well with some versions of GnuPG>=2.0.0. @@ -323,8 +352,8 @@ class GPGBase(object): :param str directory: A relative or absolute path to the directory to use for storing/accessing GnuPG's files, including keyrings and the trustdb. - :raises: :exc:`RuntimeError` if unable to find a suitable directory to - use. + :raises: :exc:`~exceptions.RuntimeError` if unable to find a suitable + directory to use. """ if not directory: log.debug("GPGBase._homedir_setter(): Using default homedir: '%s'" @@ -364,17 +393,18 @@ class GPGBase(object): def _generated_keys_setter(self, directory): """Set the directory for storing generated keys. - If unspecified, use $GNUPGHOME/generated-keys. If specified, ensure - that the ``directory`` does not contain various shell escape - characters. If ``directory`` is not found, it will be automatically - created. Lastly, the ``direcory`` will be checked that the EUID has - read and write permissions for it. + If unspecified, use + :meth:`~gnupg._meta.GPGBase.homedir`/generated-keys. If specified, + ensure that the ``directory`` does not contain various shell escape + characters. If ``directory`` isn't found, it will be automatically + created. Lastly, the ``directory`` will be checked to ensure that the + current EUID has read and write permissions for it. :param str directory: A relative or absolute path to the directory to use for storing/accessing GnuPG's files, including keyrings and the trustdb. - :raises: :exc:`RuntimeError` if unable to find a suitable directory to - use. + :raises: :exc:`~exceptions.RuntimeError` if unable to find a suitable + directory to use. """ if not directory: directory = os.path.join(self.homedir, 'generated-keys') @@ -406,10 +436,11 @@ class GPGBase(object): _generated_keys_setter) def _make_args(self, args, passphrase=False): - """Make a list of command line elements for GPG. The value of ``args`` - will be appended only if it passes the checks in - :func:`parsers._sanitise`. The ``passphrase`` argument needs to be True - if a passphrase will be sent to GPG, else False. + """Make a list of command line elements for GPG. + + The value of ``args`` will be appended only if it passes the checks in + :func:`gnupg._parsers._sanitise`. The ``passphrase`` argument needs to + be True if a passphrase will be sent to GnuPG, else False. :param list args: A list of strings of options and flags to pass to ``GPG.binary``. This is input safe, meaning that @@ -488,12 +519,13 @@ class GPGBase(object): Calls methods on the response object for each valid token found, with the arg being the remainder of the status line. - :param stream: A byte-stream, file handle, or :class:`subprocess.PIPE` - to parse the for status codes from the GnuPG process. + :param stream: A byte-stream, file handle, or a + :data:`subprocess.PIPE` for parsing the status codes + from the GnuPG process. - :param result: The result parser class from :mod:`_parsers` with which - to call ``handle_status`` and parse the output of - ``stream``. + :param result: The result parser class from :mod:`~gnupg._parsers` ― + the ``handle_status()`` method of that class will be + called in order to parse the output of ``stream``. """ lines = [] while True: @@ -534,8 +566,8 @@ class GPGBase(object): and stored as ``result.data``. :param stream: An open file-like object to read() from. - :param result: An instance of one of the result parsing classes from - :attr:`GPGBase._result_mapping`. + :param result: An instance of one of the :ref:`result parsing classes + ` from :const:`~gnupg._meta.GPGBase._result_map`. """ chunks = [] log.debug("Reading data from stream %r..." % stream.__repr__()) @@ -681,59 +713,55 @@ class GPGBase(object): cipher_algo='AES256', digest_algo='SHA512', compress_algo='ZLIB'): - """Encrypt the message read from the file-like object ``data``. + """Encrypt the message read from the file-like object **data**. :param str data: The file or bytestream to encrypt. - :param recipients: The recipients to encrypt to. Recipients must be - specified keyID/fingerprint. Care should be taken - in Python2.x to make sure that the given fingerprint - is in fact a string and not a unicode object. - :type recipients: str + :param str recipients: The recipients to encrypt to. Recipients must + be specified keyID/fingerprint. - :param default_key: The keyID/fingerprint of the key to use for - signing. If given, ``data`` will be encrypted - and signed. - :type default_key: str + .. warning:: Care should be taken in Python2 to make sure that the + given fingerprints for **recipients** are in fact strings + and not unicode objects. - :param passphrase: If given, and ``default_key`` is also given, - use this passphrase to unlock the secret portion of - the ``default_key`` to sign the encrypted ``data``. - Otherwise, if ``default_key`` is not given, but - ``symmetric=True``, then use this passphrase as the - passphrase for symmetric encryption. Signing and - symmetric encryption should *not* be combined when - sending the ``data`` to other recipients, else the - passphrase to the secret key would be shared with - them. - :type passphrase: str + :param str default_key: The keyID/fingerprint of the key to use for + signing. If given, **data** will be encrypted + *and* signed. - :param armor: If True, ascii armor the output; otherwise, the output - will be in binary format. (Default: True) - :type armor: bool + :param str passphrase: If given, and **default_key** is also given, + use this passphrase to unlock the secret + portion of the **default_key** to sign the + encrypted **data**. Otherwise, if + **default_key** is not given, but **symmetric** + is ``True``, then use this passphrase as the + passphrase for symmetric encryption. Signing + and symmetric encryption should *not* be + combined when sending the **data** to other + recipients, else the passphrase to the secret + key would be shared with them. - :param encrypt: If True, encrypt the ``data`` using the ``recipients`` - public keys. (Default: True) - :type encrypt: bool + :param bool armor: If True, ascii armor the output; otherwise, the + output will be in binary format. (Default: True) - :param symmetric: If True, encrypt the ``data`` to ``recipients`` - using a symmetric key. See the ``passphrase`` - parameter. Symmetric encryption and public key - encryption can be used simultaneously, and will - result in a ciphertext which is decryptable with - either the symmetric ``passphrase`` or one of the - corresponding private keys. - :type symmetric: bool + :param bool encrypt: If True, encrypt the **data** using the + **recipients** public keys. (Default: True) - :param always_trust: If True, ignore trust warnings on recipient - keys. If False, display trust warnings. - (default: True) - :type always_trust: bool + :param bool symmetric: If True, encrypt the **data** to **recipients** + using a symmetric key. See the **passphrase** + parameter. Symmetric encryption and public key + encryption can be used simultaneously, and will + result in a ciphertext which is decryptable + with either the symmetric **passphrase** or one + of the corresponding private keys. + + :param bool always_trust: If True, ignore trust warnings on + **recipients** keys. If False, display trust + warnings. (default: True) + + :param str output: The output file to write to. If not specified, the + encrypted output is returned, and thus should be + stored as an object in Python. For example: - :param output: The output file to write to. If not specified, the - encrypted output is returned, and thus should be - stored as an object in Python. For example: - :type output: str >>> import shutil >>> import gnupg @@ -756,21 +784,19 @@ class GPGBase(object): :param str cipher_algo: The cipher algorithm to use. To see available algorithms with your version of GnuPG, do: - ``$ gpg --with-colons --list-config - ciphername``. - The default ``cipher_algo``, if unspecified, - is ``'AES256'``. - :type cipher_algo: str + :command:`$ gpg --with-colons --list-config + ciphername`. The default **cipher_algo**, if + unspecified, is ``'AES256'``. - :param digest_algo: The hash digest to use. Again, to see which - hashes your GnuPG is capable of using, do: - ``$ gpg --with-colons --list-config digestname``. - The default, if unspecified, is ``'SHA512'``. - :type digest_algo: str + :param str digest_algo: The hash digest to use. Again, to see which + hashes your GnuPG is capable of using, do: + :command:`$ gpg --with-colons --list-config + digestname`. The default, if unspecified, is + ``'SHA512'``. - :param compress_algo: The compression algorithm to use. Can be one - :type compress_algo: str - of ``'ZLIB'``, ``'BZIP2'``, ``'ZIP'``, or ``'Uncompressed'``. + :param str compress_algo: The compression algorithm to use. Can be one + of ``'ZLIB'``, ``'BZIP2'``, ``'ZIP'``, or + ``'Uncompressed'``. """ args = [] From 82cdfaf45e81956e7a2197b2e0152707dd89cfd9 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 22:26:26 +0000 Subject: [PATCH 36/56] Cleanup Sphinx directives in docstrings in _parsers.py. --- gnupg/_parsers.py | 115 +++++++++++++++++++++++++--------------------- 1 file changed, 63 insertions(+), 52 deletions(-) diff --git a/gnupg/_parsers.py b/gnupg/_parsers.py index 9c09002..928e7b2 100644 --- a/gnupg/_parsers.py +++ b/gnupg/_parsers.py @@ -50,7 +50,7 @@ def _check_keyserver(location): should contain the desired keyserver protocol which is supported by the keyserver, for example, the default is ``'hkp://subkeys.pgp.net'``. - :rtype: str or None + :rtype: :obj:`str` or :obj:`None` :returns: A string specifying the protocol and keyserver hostname, if the checks passed. If not, returns None. """ @@ -73,10 +73,11 @@ def _check_keyserver(location): def _check_preferences(prefs, pref_type=None): """Check cipher, digest, and compression preference settings. - MD5 is not allowed. This is not 1994.[0] SHA1 is allowed grudgingly.[1] + MD5 is not allowed. This is `not 1994`__. SHA1 is allowed_ grudgingly_. - [0]: http://www.cs.colorado.edu/~jrblack/papers/md5e-full.pdf - [1]: http://eprint.iacr.org/2008/469.pdf + __ http://www.cs.colorado.edu/~jrblack/papers/md5e-full.pdf + .. _allowed: http://eprint.iacr.org/2008/469.pdf + .. _grudgingly: https://www.schneier.com/blog/archives/2012/10/when_will_we_se.html """ if prefs is None: return @@ -114,8 +115,8 @@ def _check_preferences(prefs, pref_type=None): return allowed def _fix_unsafe(shell_input): - """Find characters used to escape from a string into a shell, and wrap them - in quotes if they exist. Regex pilfered from python-3.x shlex module. + """Find characters used to escape from a string into a shell, and wrap them in + quotes if they exist. Regex pilfered from Python3 :mod:`shlex` module. :param str shell_input: The input intended for the GnuPG process. """ @@ -160,11 +161,15 @@ def _is_allowed(input): class and its name will need to be added to this set. - :rtype: Exception or str - :raise: :exc:UsageError if ``_allowed`` is not a subset of ``_possible``. - ProtectedOption if ``input`` is not in the set ``_allowed``. - :return: The original parameter ``input``, unmodified and unsanitized, - if no errors occur. + :raises: :exc:`UsageError` if **input** is not a subset of the hard-coded + set of all GnuPG options in :func:`_get_all_gnupg_options`. + + :exc:`ProtectedOption` if **input** is not in the set of allowed + options. + + :rtype: str + :return: The original **input** parameter, unmodified and unsanitized, if + no errors occur. """ gnupg_options = _get_all_gnupg_options() allowed = _get_options_group("allowed") @@ -219,12 +224,12 @@ def _is_hex(string): def _is_string(thing): """Python character arrays are a mess. - If Python2, check if ``thing`` is a ``unicode()`` or ``str()``. - If Python3, check if ``thing`` is a ``str()``. + If Python2, check if **thing** is an :obj:`unicode` or a :obj:`str`. + If Python3, check if **thing** is a :obj:`str`. :param thing: The thing to check. - :returns: ``True`` if ``thing`` is a "string" according to whichever - version of Python we're running in. + :returns: ``True`` if **thing** is a string according to whichever version + of Python we're running in. """ if _util._py3k: return isinstance(thing, str) else: return isinstance(thing, basestring) @@ -237,18 +242,20 @@ def _sanitise(*args): sanitised, allowed options. Each new option that we support that is not a boolean, but instead has - some extra inputs, i.e. "--encrypt-file foo.txt", will need some basic - safety checks added here. + some additional inputs following it, i.e. "--encrypt-file foo.txt", will + need some basic safety checks added here. GnuPG has three-hundred and eighteen commandline flags. Also, not all implementations of OpenPGP parse PGP packets and headers in the same way, so there is added potential there for messing with calls to GPG. - For information on the PGP message format specification, see: - https://www.ietf.org/rfc/rfc1991.txt + For information on the PGP message format specification, see + :rfc:`1991`. If you're asking, "Is this *really* necessary?": No, not really -- we could - just do a check as described here: https://xkcd.com/1181/ + just follow the security precautions recommended by `this xkcd`__. + + __ https://xkcd.com/1181/ :param str args: (optional) The boolean arguments which will be passed to the GnuPG process. @@ -259,22 +266,23 @@ def _sanitise(*args): ## see TODO file, tag :cleanup:sanitise: def _check_option(arg, value): - """ - Check that a single :param:arg is an allowed option. If it is allowed, - quote out any escape characters in :param:values, and add the pair to - :ivar:sanitised. + """Check that a single ``arg`` is an allowed option. + + If it is allowed, quote out any escape characters in ``value``, and + add the pair to :ivar:`sanitised`. Otherwise, drop them. :param str arg: The arguments which will be passed to the GnuPG process, and, optionally their corresponding values. The values are any additional arguments following the - GnuPG option or flag. For example, if we wanted to pass - "--encrypt --recipient isis@leap.se" to gpg, then - "--encrypt" would be an arg without a value, and - "--recipient" would also be an arg, with a value of - "isis@leap.se". + GnuPG option or flag. For example, if we wanted to + pass ``"--encrypt --recipient isis@leap.se"`` to + GnuPG, then ``"--encrypt"`` would be an arg without a + value, and ``"--recipient"`` would also be an arg, + with a value of ``"isis@leap.se"``. + :ivar list checked: The sanitised, allowed options and values. :rtype: str - :returns: A string of the items in ``checked`` delimited by spaces. + :returns: A string of the items in ``checked``, delimited by spaces. """ checked = str() none_options = _get_options_group("none_options") @@ -573,21 +581,22 @@ def _get_all_gnupg_options(): This is hardcoded within a local scope to reduce the chance of a tampered GnuPG binary reporting falsified option sets, i.e. because certain options - (namedly the '--no-options' option, which prevents the usage of gpg.conf + (namedly the ``--no-options`` option, which prevents the usage of gpg.conf files) are necessary and statically specified in - :meth:`gnupg.GPG._makeargs`, if the inputs into Python are already - controlled, and we were to summon the GnuPG binary to ask it for its - options, it would be possible to receive a falsified options set missing - the '--no-options' option in response. This seems unlikely, and the method - is stupid and ugly, but at least we'll never have to debug whether or not - an option *actually* disappeared in a different GnuPG version, or some - funny business is happening. + :meth:`gnupg._meta.GPGBase._make_args`, if the inputs into Python are + already controlled, and we were to summon the GnuPG binary to ask it for + its options, it would be possible to receive a falsified options set + missing the ``--no-options`` option in response. This seems unlikely, and + the method is stupid and ugly, but at least we'll never have to debug + whether or not an option *actually* disappeared in a different GnuPG + version, or some funny business is happening. These are the options as of GnuPG 1.4.12; the current stable branch of the 2.1.x tree contains a few more -- if you need them you'll have to add them in here. - :ivar frozenset gnupg_options: All known GPG options and flags. + :type gnupg_options: frozenset + :ivar gnupg_options: All known GPG options and flags. :rtype: frozenset :returns: ``gnupg_options`` """ @@ -790,8 +799,8 @@ def progress(status_code): class GenKey(object): """Handle status messages for key generation. - Calling the GenKey.__str__() method of this class will return the - generated key's fingerprint, or a status string explaining the results. + Calling the ``__str__()`` method of this class will return the generated + key's fingerprint, or a status string explaining the results. """ def __init__(self, gpg): self._gpg = gpg @@ -802,11 +811,13 @@ class GenKey(object): self.status = None self.subkey_created = False self.primary_created = False - #: This will store the filename of the key's public keyring if - #: :meth:`GPG.gen_key_input` was called with ``separate_keyring=True`` + #: This will store the key's public keyring filename, if + #: :meth:`~gnupg.GPG.gen_key_input` was called with + #: ``separate_keyring=True``. self.keyring = None - #: This will store the filename of the key's secret keyring if - #: :meth:`GPG.gen_key_input` was called with ``separate_keyring=True`` + #: This will store the key's secret keyring filename, if : + #: :meth:`~gnupg.GPG.gen_key_input` was called with + #: ``separate_keyring=True``. self.secring = None def __nonzero__(self): @@ -826,7 +837,7 @@ class GenKey(object): def _handle_status(self, key, value): """Parse a status code from the attached GnuPG process. - :raises: :exc:`ValueError` if the status message is unknown. + :raises: :exc:`~exceptions.ValueError` if the status message is unknown. """ if key in ("GOOD_PASSPHRASE"): pass @@ -863,7 +874,7 @@ class DeleteResult(object): def _handle_status(self, key, value): """Parse a status code from the attached GnuPG process. - :raises: :exc:`ValueError` if the status message is unknown. + :raises: :exc:`~exceptions.ValueError` if the status message is unknown. """ if key == "DELETE_PROBLEM": self.status = self.problem_reason.get(value, "Unknown error: %r" @@ -908,7 +919,7 @@ class Sign(object): def _handle_status(self, key, value): """Parse a status code from the attached GnuPG process. - :raises: :exc:`ValueError` if the status message is unknown. + :raises: :exc:`~exceptions.ValueError` if the status message is unknown. """ if key in ("USERID_HINT", "NEED_PASSPHRASE", "BAD_PASSPHRASE", "GOOD_PASSPHRASE", "BEGIN_SIGNING", "CARDCTRL", @@ -1040,7 +1051,7 @@ class ImportResult(object): def _handle_status(self, key, value): """Parse a status code from the attached GnuPG process. - :raises: :exc:`ValueError` if the status message is unknown. + :raises: :exc:`~exceptions.ValueError` if the status message is unknown. """ if key == "IMPORTED": # this duplicates info we already see in import_ok & import_problem @@ -1186,7 +1197,7 @@ class Verify(object): def _handle_status(self, key, value): """Parse a status code from the attached GnuPG process. - :raises: :exc:`ValueError` if the status message is unknown. + :raises: :exc:`~exceptions.ValueError` if the status message is unknown. """ if key in self.TRUST_LEVELS: self.trust_text = key @@ -1281,7 +1292,7 @@ class Crypt(Verify): def _handle_status(self, key, value): """Parse a status code from the attached GnuPG process. - :raises: :exc:`ValueError` if the status message is unknown. + :raises: :exc:`~exceptions.ValueError` if the status message is unknown. """ if key in ("ENC_TO", "USERID_HINT", "GOODMDC", "END_DECRYPTION", "BEGIN_SIGNING", "NO_SECKEY", "ERROR", "NODATA", @@ -1348,7 +1359,7 @@ class ListPackets(object): def _handle_status(self, key, value): """Parse a status code from the attached GnuPG process. - :raises: :exc:`ValueError` if the status message is unknown. + :raises: :exc:`~exceptions.ValueError` if the status message is unknown. """ if key == 'NODATA': self.status = nodata(value) From 04b9a13a515114cf899c858122e71b51e46f3226 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 22:27:11 +0000 Subject: [PATCH 37/56] Cleanup docstrings in _util.py. --- gnupg/_util.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gnupg/_util.py b/gnupg/_util.py index eb76190..6f5402c 100644 --- a/gnupg/_util.py +++ b/gnupg/_util.py @@ -27,6 +27,7 @@ from time import mktime import codecs import encodings +import exceptions import os import psutil import threading @@ -252,7 +253,8 @@ def _find_binary(binary=None): our process real uid has exec permissions. :param str binary: The path to the GnuPG binary. - :raises: :exc:RuntimeError if it appears that GnuPG is not installed. + :raises: :exc:`~exceptions.RuntimeError` if it appears that GnuPG is not + installed. :rtype: str :returns: The absolute path to the GnuPG binary to use, if no exceptions occur. @@ -516,7 +518,7 @@ def _which(executable, flags=os.X_OK): def _write_passphrase(stream, passphrase, encoding): """Write the passphrase from memory to the GnuPG process' stdin. - :type stream: file, :class:BytesIO, or :class:StringIO + :type stream: file, :class:`~io.BytesIO`, or :class:`~io.StringIO` :param stream: The input file descriptor to write the password to. :param str passphrase: The passphrase for the secret key material. :param str encoding: The data encoding expected by GnuPG. Usually, this From 641f503f4a44efdc026077cec36b43506637d3a7 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 22:35:17 +0000 Subject: [PATCH 38/56] Cleanup docstrings in gnupg.py. --- gnupg/gnupg.py | 110 +++++++++++++++++++++++++------------------------ 1 file changed, 57 insertions(+), 53 deletions(-) diff --git a/gnupg/gnupg.py b/gnupg/gnupg.py index 0ad2cd0..61c97ed 100644 --- a/gnupg/gnupg.py +++ b/gnupg/gnupg.py @@ -21,16 +21,17 @@ =========== A Python interface to GnuPG. -.. moduleauthor:: Isis Agora Lovecruft +.. moduleauthor:: Isis Lovecruft see also :attr:`gnupg.__authors__` -:license: see :attr:`gnupg.__license__` -:info: see +.. license:: see :attr:`gnupg.__license__` +.. info:: https://github.com/isislovecruft/python-gnupg """ from __future__ import absolute_import from codecs import open as open import encodings +import exceptions import functools import os import re @@ -41,7 +42,7 @@ try: except ImportError: from cStringIO import StringIO -## see PEP-328 http://docs.python.org/2.5/whatsnew/pep-328.html +#: see :pep:`328` http://docs.python.org/2.5/whatsnew/pep-328.html from . import _parsers from . import _util from . import _trust @@ -70,35 +71,36 @@ class GPG(GPGBase): """Initialize a GnuPG process wrapper. :param str binary: Name for GnuPG binary executable. If the absolute - path is not given, the evironment variable $PATH is - searched for the executable and checked that the - real uid/gid of the user has sufficient permissions. + path is not given, the environment variable + ``$PATH`` is searched for the executable and + checked that the real uid/gid of the user has + sufficient permissions. :param str homedir: Full pathname to directory containing the public and private keyrings. Default is whatever GnuPG defaults to. - :param str,int,bool verbose: String or numeric value to pass to gpg's - ``--debug-level`` option. See the gpg man - page for the list of valid options. If - False, debug output is not generated by - the gpg binary. If True, defaults to - ``--debug-level basic.`` + :type verbose: :obj:`str` or :obj:`int` or :obj:`bool` + :param verbose: String or numeric value to pass to GnuPG's + ``--debug-level`` option. See the GnuPG man page for + the list of valid options. If False, debug output is + not generated by the GnuPG binary. If True, defaults + to ``--debug-level basic.`` - :param str keyring: Name of keyring file containing public key data, if - unspecified, defaults to 'pubring.gpg' in the - ``homedir`` directory. + :param str keyring: Name of keyring file containing public key data. + If unspecified, defaults to :file:`pubring.gpg` in + the **homedir** directory. :param str secring: Name of alternative secret keyring file to use. If left unspecified, this will default to using - 'secring.gpg' in the :param:homedir directory, and - create that file if it does not exist. + :file:`secring.gpg` in the **homedir** directory, + and create that file if it does not exist. - :param list options: A list of additional options to pass to the GPG + :param list options: A list of additional options to pass to the GnuPG binary. - :raises: :exc:`RuntimeError` with explanation message if there is a - problem invoking gpg. + :raises: A :exc:`~exceptions.RuntimeError` with explanation message + if there is a problem invoking GnuPG. Example: @@ -227,7 +229,7 @@ class GPG(GPGBase): In simpler terms: this function isn't for signing your friends' keys, it's for something like signing an email. - :type data: str or file + :type data: :obj:`str` or :obj:`file` :param data: A string or file stream to sign. :param str default_key: The key to sign with. :param str passphrase: The passphrase to pipe to stdin. @@ -236,7 +238,7 @@ class GPG(GPGBase): :param bool binary: If True, do not ascii armour the output. :param str digest_algo: The hash digest to use. Again, to see which hashes your GnuPG is capable of using, do: - ``$ gpg --with-colons --list-config digestname``. + :command:`$ gpg --with-colons --list-config digestname`. The default, if unspecified, is ``'SHA512'``. """ if 'default_key' in kwargs.items(): @@ -384,11 +386,11 @@ class GPG(GPGBase): def delete_keys(self, fingerprints, secret=False, subkeys=False): """Delete a key, or list of keys, from the current keyring. - The keys must be refered to by their full fingerprint for GnuPG to + The keys must be referred to by their full fingerprints for GnuPG to delete them. If ``secret=True``, the corresponding secret keyring will - be deleted from :attr:`GPG.secring`. + be deleted from :obj:`.secring`. - :type fingerprints: str or list or tuple + :type fingerprints: :obj:`str` or :obj:`list` or :obj:`tuple` :param fingerprints: A string, or a list/tuple of strings, representing the fingerprint(s) for the key(s) to delete. @@ -398,8 +400,7 @@ class GPG(GPGBase): :param bool subkeys: If True, delete the secret subkey first, then the public key. (default: False) Same as: - ``$ gpg --delete-secret-and-public-key - 0x12345678`` + :command:`$gpg --delete-secret-and-public-key 0x12345678`. """ which = 'keys' if secret: @@ -548,7 +549,7 @@ class GPG(GPGBase): :param dict input: A dictionary of parameters and values for the new key. :returns: The result mapping with details of the new key, which is a - :class:`parsers.GenKey ` object. + :class:`GenKey ` object. """ args = ["--gen-key --batch"] key = self._result_map['generate'](self) @@ -585,11 +586,11 @@ class GPG(GPGBase): def gen_key_input(self, separate_keyring=False, save_batchfile=False, testing=False, **kwargs): - """Generate a batch file for input to :meth:`GPG.gen_key()`. + """Generate a batch file for input to :meth:`~gnupg.GPG.gen_key`. The GnuPG batch file key generation feature allows unattended key generation by creating a file with special syntax and then providing it - to: ``gpg --gen-key --batch``. Batch files look like this: + to: :command:`gpg --gen-key --batch`. Batch files look like this: | Name-Real: Alice | Name-Email: alice@inter.net @@ -648,9 +649,10 @@ class GPG(GPGBase): :param bool separate_keyring: Specify for the new key to be written to a separate pubring.gpg and secring.gpg. If True, - :meth:`GPG.gen_key` will automatically rename the separate keyring - and secring to whatever the fingerprint of the generated key ends - up being, suffixed with '.pubring' and '.secring' respectively. + :meth:`~gnupg.GPG.gen_key` will automatically rename the separate + keyring and secring to whatever the fingerprint of the generated + key ends up being, suffixed with '.pubring' and '.secring' + respectively. :param bool save_batchfile: Save a copy of the generated batch file to disk in a file named .batch, where is the @@ -664,11 +666,12 @@ class GPG(GPGBase): :param str name_real: The name field of the UID in the generated key. :param str name_comment: The comment in the UID of the generated key. + :param str name_email: The email in the UID of the generated key. - (default: $USER@$(hostname) ) Remember to use UTF-8 encoding for - the entirety of the UID. At least one of ``name_real``, - ``name_comment``, or ``name_email`` must be provided, or else no - user ID is created. + (default: ``$USER`` @ :command:`hostname` ) Remember to use UTF-8 + encoding for the entirety of the UID. At least one of + ``name_real``, ``name_comment``, or ``name_email`` must be + provided, or else no user ID is created. :param str key_type: One of 'RSA', 'DSA', 'ELG-E', or 'default'. (default: 'RSA', if using GnuPG v1.x, otherwise 'default') Starts @@ -707,7 +710,7 @@ class GPG(GPGBase): :param str subkey_usage: Key usage for a subkey; similar to ``key_usage``. - :type expire_date: int or str + :type expire_date: :obj:`int` or :obj:`str` :param expire_date: Can be specified as an iso-date or as [d|w|m|y] Set the expiration date for the key (and the subkey). It may either be entered in ISO date format (2000-08-15) @@ -729,17 +732,17 @@ class GPG(GPGBase): :param str passphrase: The passphrase for the new key. The default is to not use any passphrase. Note that GnuPG>=2.1.x will not allow you to specify a passphrase for batch key generation -- GnuPG will - ignore the ``passphrase`` parameter, stop, and ask the user for + ignore the **passphrase** parameter, stop, and ask the user for the new passphrase. However, we can put the command - '%no-protection' into the batch key generation file to allow a + ``%no-protection`` into the batch key generation file to allow a passwordless key to be created, which can then have its passphrase - set later with '--edit-key'. + set later with ``--edit-key``. :param str preferences: Set the cipher, hash, and compression preference values for this key. This expects the same type of string as the sub-command ‘setpref’ in the --edit-key menu. - :param str revoker: Should be given as 'algo:fpr' [case sensitive]. + :param str revoker: Should be given as 'algo:fpr' (case sensitive). Add a designated revoker to the generated key. Algo is the public key algorithm of the designated revoker (i.e. RSA=1, DSA=17, etc.) fpr is the fingerprint of the designated revoker. The optional @@ -750,18 +753,18 @@ class GPG(GPGBase): preferred keyserver URL for the key. :param str handle: This is an optional parameter only used with the - status lines KEY_CREATED and KEY_NOT_CREATED. string may be up to - 100 characters and should not contain spaces. It is useful for - batch key generation to associate a key parameter block with a - status line. + status lines ``KEY_CREATED`` and ``KEY_NOT_CREATED``. string may + be up to 100 characters and should not contain spaces. It is + useful for batch key generation to associate a key parameter block + with a status line. :rtype: str :returns: A suitable input string for the :meth:`GPG.gen_key` method, the latter of which will create the new keypair. - see - http://www.gnupg.org/documentation/manuals/gnupg-devel/Unattended-GPG-key-generation.html - for more details. + See `this GnuPG Manual section`__ for more details. + + __ http://www.gnupg.org/documentation/manuals/gnupg-devel/Unattended-GPG-key-generation.html """ #: A boolean for determining whether to set subkey_type to 'default' default_type = False @@ -934,20 +937,21 @@ generate keys. Please see >>> decrypted 'The crow flies at midnight.' + :param str cipher_algo: The cipher algorithm to use. To see available algorithms with your version of GnuPG, do: - ``$ gpg --with-colons --list-config ciphername``. + :command:`$ gpg --with-colons --list-config ciphername`. The default ``cipher_algo``, if unspecified, is ``'AES256'``. :param str digest_algo: The hash digest to use. Again, to see which hashes your GnuPG is capable of using, do: - ``$ gpg --with-colons --list-config digestname``. + :command:`$ gpg --with-colons --list-config digestname`. The default, if unspecified, is ``'SHA512'``. :param str compress_algo: The compression algorithm to use. Can be one of ``'ZLIB'``, ``'BZIP2'``, ``'ZIP'``, or ``'Uncompressed'``. - See also: :meth:`GPGBase._encrypt` + .. seealso:: :meth:`._encrypt` """ stream = _make_binary_stream(data, self._encoding) result = self._encrypt(stream, recipients, **kwargs) From 97933ebb0723cdb78ec1e599c2ab7bb1689c3586 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Tue, 3 Dec 2013 22:35:38 +0000 Subject: [PATCH 39/56] Get rid of the annoying "GnuPG logging disabled..." print statement. --- gnupg/_logger.py | 1 - 1 file changed, 1 deletion(-) diff --git a/gnupg/_logger.py b/gnupg/_logger.py index 1f807d5..6f3b74c 100644 --- a/gnupg/_logger.py +++ b/gnupg/_logger.py @@ -92,7 +92,6 @@ def create_logger(level=logging.NOTSET): print("Starting the logger...") else: handler = NullHandler() - print("GnuPG logging disabled...") log = logging.getLogger('gnupg') log.addHandler(handler) From cc59df2ca30332bcd36358833d8b07f245cdda6e Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 4 Dec 2013 00:31:41 +0000 Subject: [PATCH 40/56] Check for Py2.6 in setup.py; then add ordereddict to install_requires. --- gnupg/_parsers.py | 9 +++------ setup.py | 11 +++++++++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/gnupg/_parsers.py b/gnupg/_parsers.py index dd3ddaa..c625d75 100644 --- a/gnupg/_parsers.py +++ b/gnupg/_parsers.py @@ -25,12 +25,9 @@ from __future__ import absolute_import from __future__ import print_function try: - # py2.7+ comes with collections.OrderedDict - from collections import OrderedDict + import collections except ImportError: - # py2.6 doesn't; this is an equivalent; - # `pip install ordereddict` - from ordereddict import OrderedDict + import ordereddict as collections import re @@ -1029,7 +1026,7 @@ class ImportResult(object): _fields = '''count no_user_id imported imported_rsa unchanged n_uids n_subk n_sigs n_revoc sec_read sec_imported sec_dups not_imported'''.split() - _counts = OrderedDict( + _counts = collections.OrderedDict( zip(_fields, [int(0) for x in range(len(_fields))]) ) #: A list of strings containing the fingerprints of the GnuPG keyIDs diff --git a/setup.py b/setup.py index 3b4154d..90b1e8e 100644 --- a/setup.py +++ b/setup.py @@ -23,6 +23,7 @@ from __future__ import absolute_import from __future__ import print_function import setuptools +import sys import os import versioneer @@ -37,6 +38,12 @@ __contact__ = 'isis@patternsinthevoid.net' __url__ = 'https://github.com/isislovecruft/python-gnupg' +def python26(): + """Returns True if we're running on Python2.6.""" + if sys.version[:3] == "2.6": + return True + return False + def get_requirements(): """Extract the list of requirements from our requirements.txt. @@ -64,6 +71,10 @@ def get_requirements(): except (IOError, OSError) as error: print(error) + if python26(): + # Required to make `collections.OrderedDict` available on Python<=2.6 + requirements.append('ordereddict==1.1#a0ed854ee442051b249bfad0f638bbec') + return requirements, links From 0a13554d9296d58404a934c525fb56bef7cbd6bc Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 4 Dec 2013 00:34:32 +0000 Subject: [PATCH 41/56] Remove dependency_link print statements from setup.py. --- setup.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/setup.py b/setup.py index 90b1e8e..6f8a1ca 100644 --- a/setup.py +++ b/setup.py @@ -79,11 +79,6 @@ def get_requirements(): requires, deplinks = get_requirements() -print('Found requirements:') -[print('\t%s' % name) for name in requires] - -print('Found dependency links:') -[print('\t%s' % uri) for uri in deplinks] setuptools.setup( From 48a43435f01ba1c4ca9917752e927dd3bfbbe807 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 4 Dec 2013 00:35:28 +0000 Subject: [PATCH 42/56] =?UTF-8?q?Update=20setup.py=20docs=20requires:=20re?= =?UTF-8?q?poze.sphinx=20=E2=86=92=20sphinxcontrib-fulltoc.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 6f8a1ca..a4cd216 100644 --- a/setup.py +++ b/setup.py @@ -111,7 +111,8 @@ greater. install_requires=requires, dependency_links=deplinks, - extras_require={'docs': ["Sphinx>=1.1", "repoze.sphinx"]}, + extras_require={'docs': ["Sphinx>=1.1", + "sphinxcontrib-fulltoc==1.0"]}, platforms="Linux, BSD, OSX, Windows", download_url="https://github.com/isislovecruft/python-gnupg/archive/master.zip", From af93c4caaffdcb07aba2a4c4b3e516ff831d43d3 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 4 Dec 2013 08:48:56 +0000 Subject: [PATCH 43/56] Remove unicode characters preventing manpage and LaTeX Sphinx builds. --- gnupg/_trust.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnupg/_trust.py b/gnupg/_trust.py index 9f72ac6..62d8fc6 100644 --- a/gnupg/_trust.py +++ b/gnupg/_trust.py @@ -82,7 +82,7 @@ def fix_trustdb(cls, trustdb=None): it would fix the the trustdb. Hah! It doesn't. Here's what it does instead:: - (gpg)∃!isisⒶwintermute:(testing/algo)~/code/python-gnupg ∴ gpg2 --fix-trustdb + (gpg)~/code/python-gnupg $ gpg2 --fix-trustdb gpg: You may try to re-create the trustdb using the commands: gpg: cd ~/.gnupg gpg: gpg2 --export-ownertrust > otrust.tmp From b90d3b6414b5d7f84ee01f312a8ad0f30db3c0b1 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 4 Dec 2013 10:01:05 +0000 Subject: [PATCH 44/56] Clarify a bit of and/or logic in _parsers._check_options(). --- gnupg/_parsers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gnupg/_parsers.py b/gnupg/_parsers.py index c625d75..58a681c 100644 --- a/gnupg/_parsers.py +++ b/gnupg/_parsers.py @@ -343,8 +343,9 @@ def _sanitise(*args): if flag in ['--encrypt', '--encrypt-files', '--decrypt', '--decrypt-files', '--import', '--verify']: - if _util._is_file(val) or \ - (flag == '--verify' and val == '-'): + if ( (_util._is_file(val)) + or + ((flag == '--verify') and (val == '-')) ): checked += (val + " ") else: log.debug("%s not file: %s" % (flag, val)) From f5b345b1aefe8e5bd98e712dace845fa7487457b Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 4 Dec 2013 10:02:08 +0000 Subject: [PATCH 45/56] Refactor _util._is_file to handle detached sigs and data on stdin. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When gnupg.GPG.verify_file() was changed recently to take the filename of the signature file as an argument to GnuPG, and then take the data file to be verified on stdin, the parser in _parsers._check_options() would call _util._is_file(), which would return False and log an error that "'-' is not a file!". This fixes that issue by catching OSError and treating it differently. Additionally, I renamed the _util._is_file() parameter `input`→`filename` to avoid overriding a builtin method. I also replaced the `assert` lines with explicit checks, because `assert`s are stripped out when the Python interpreter is run with -OO. * FIXES an issue with verification of detached signatures whose datafiles are read from stdin. --- gnupg/_util.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/gnupg/_util.py b/gnupg/_util.py index 6f5402c..179ecbf 100644 --- a/gnupg/_util.py +++ b/gnupg/_util.py @@ -303,22 +303,31 @@ def _has_readwrite(path): """ return os.access(path, os.R_OK ^ os.W_OK) -def _is_file(input): +def _is_file(filename): """Check that the size of the thing which is supposed to be a filename has size greater than zero, without following symbolic links or using :func:os.path.isfile. - :param input: An object to check. + :param filename: An object to check. :rtype: bool - :returns: True if :param:input is file-like, False otherwise. + :returns: True if **filename** is file-like, False otherwise. """ try: - assert os.lstat(input).st_size > 0, "not a file: %s" % input - except (AssertionError, TypeError, IOError, OSError) as err: - log.error(err.message, exc_info=1) - return False + statinfo = os.lstat(filename) + log.debug("lstat(%r) with type=%s gave us %r" + % (repr(filename), type(filename), repr(statinfo))) + if not (statinfo.st_size > 0): + raise ValueError("'%s' appears to be an empty file!" % filename) + except OSError as oserr: + log.error(oserr) + if filename == '-': + log.debug("Got '-' for filename, assuming sys.stdin...") + return True + except (ValueError, TypeError, IOError) as err: + log.error(err) else: return True + return False def _is_stream(input): """Check that the input is a byte stream. From 56fab92b485b55e55c4b3ad749dee42c86839bb3 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 4 Dec 2013 10:10:18 +0000 Subject: [PATCH 46/56] Fix issue with detached sig verification not writing verify output. In the method `gnupg.GPG.verify_file()`, the `writer` was improperly initialised: `_util._threaded_copy_data` was being given a filename when it expects an open `file` object which it can read() from. Additionally, the `writer` parameter was missing from the call to `gnupg.GPG._collect_output()`, so even if it had been properly initialised, the output would not have been written to the stdin of the thread GnuPG was being called within. --- gnupg/gnupg.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/gnupg/gnupg.py b/gnupg/gnupg.py index 61c97ed..3053d17 100644 --- a/gnupg/gnupg.py +++ b/gnupg/gnupg.py @@ -308,15 +308,19 @@ class GPG(GPGBase): return result log.debug('verify_file(): Handling detached verification') sig_fh = None + data_fh = None try: - sig_fh = open(sig_file) + sig_fh = open(sig_file, 'rb') + data_fh = open(file, 'rb') args = ["--verify %s -" % sig_fh.name] proc = self._open_subprocess(args) - writer = _util._threaded_copy_data(file, proc.stdin) - self._collect_output(proc, result, stdin=proc.stdin) + writer = _util._threaded_copy_data(data_fh, proc.stdin) + self._collect_output(proc, result, writer, stdin=proc.stdin) finally: if sig_fh and not sig_fh.closed: sig_fh.close() + if data_fh and not data_fh.closed: + data_fh.close() return result def import_keys(self, key_data): From 425d116ae0ad3557299b09e3fe981560cbeb93ec Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 4 Dec 2013 10:16:04 +0000 Subject: [PATCH 47/56] Rewrite verification unittests to use correct parameters --- gnupg/test/test_gnupg.py | 88 ++++++++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 30 deletions(-) diff --git a/gnupg/test/test_gnupg.py b/gnupg/test/test_gnupg.py index e223dbc..1f3b37a 100644 --- a/gnupg/test/test_gnupg.py +++ b/gnupg/test/test_gnupg.py @@ -672,44 +672,72 @@ class GPGTestCase(unittest.TestCase): def test_signature_verification_detached(self): """Test that verification of a detached signature of a file works.""" + key = self.generate_key("Paulo S.L.M. Barreto", "anub.is") - with open(os.path.join(_files, 'cypherpunk_manifesto'), 'rb') as cm: - sig = self.gpg.sign(cm, default_key=key.fingerprint, - passphrase='paulos.l.m.barreto', - detach=True, clearsign=False) - self.assertTrue(sig.data, "File signing should succeed") - sigfilename = os.path.join(_files, 'cypherpunk_manifesto.sig') - with open(sigfilename,'w') as sigfile: - sigfile.write(sig.data) - sigfile.seek(0) + datafn = os.path.join(_files, 'cypherpunk_manifesto') + sigfn = os.path.extsep.join([datafn, 'sig']) - verified = self.gpg.verify_file(cm, sigfilename) + datafd = open(datafn, 'rb') + sig = self.gpg.sign(datafd, default_key=key.fingerprint, + passphrase='paulos.l.m.barreto', + detach=True, + clearsign=False) - if key.fingerprint != verified.fingerprint: - log.warn("key fingerprint: %r", key.fingerprint) - log.warn("verified fingerprint: %r", verified.fingerprint) - self.assertEqual(key.fingerprint, verified.fingerprint) + self.assertTrue(sig.data, "File signing should succeed") - if os.path.isfile(sigfilename): - os.unlink(sigfilename) + sigfd = open(sigfn, 'w') + sigfd.write(sig.data) + sigfd.flush() + + datafd.seek(0) + sigfd.seek(0) + + verified = self.gpg.verify_file(datafn, sigfn) + + if key.fingerprint != verified.fingerprint: + log.warn("key fingerprint: %r", key.fingerprint) + log.warn("verified fingerprint: %r", verified.fingerprint) + self.assertEqual(key.fingerprint, verified.fingerprint) + + if os.path.isfile(sigfn): + os.unlink(sigfn) def test_signature_verification_detached_binary(self): """Test that detached signature verification in binary mode fails.""" + key = self.generate_key("Adi Shamir", "rsa.com") - datafile = os.path.join(_files, 'cypherpunk_manifesto') - with open(datafile, 'rb') as cm: - sig = self.gpg.sign(cm, default_key=key.fingerprint, - passphrase='adishamir', - detach=True, binary=True, clearsign=False) - self.assertTrue(sig.data, "File signing should succeed") - with open(datafile+'.sig', 'w') as bs: - bs.write(sig.data) - bs.flush() - with self.assertRaises(UnicodeDecodeError): - print("SIG=%s" % sig) - with open(datafile+'.sig', 'rb') as fsig: - with open(datafile, 'rb') as fdata: - self.gpg.verify_file(fdata, fsig) + datafn = os.path.join(_files, 'cypherpunk_manifesto') + sigfn = os.path.extsep.join([datafn, 'sig']) + + datafd = open(datafn, 'rb') + data = datafd.read() + datafd.close() + + sig = self.gpg.sign(data, default_key=key.fingerprint, + passphrase='adishamir', + detach=True, + binary=True, + clearsign=False) + + self.assertTrue(sig.data, "File signing should succeed") + + sigfd = open(sigfn, 'wb') + sigfd.write(sig.data) + sigfd.flush() + sigfd.close() + + self.assertTrue(sigfd.closed, "Sigfile '%s' should be closed" % sigfn) + with self.assertRaises(UnicodeDecodeError): + print("SIG=%s" % sig) + + verifysig = open(sigfn, 'rb') + verification = self.gpg.verify_file(data, verifysig) + + self.assertTrue(isinstance(verification, gnupg._parsers.Verify)) + self.assertFalse(verification.valid) + + if os.path.isfile(sigfn): + os.unlink(sigfn) def test_deletion(self): """Test that key deletion works.""" From 7bf4246c177a942b448e0ec7311f1d17f259b351 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Wed, 4 Dec 2013 11:34:49 +0000 Subject: [PATCH 48/56] Fix setup.cfg 'upload_all' alias. --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 530e4e1..9103812 100644 --- a/setup.cfg +++ b/setup.cfg @@ -8,5 +8,5 @@ sign = True identity = 0xa3adb67a2cdb8b35 [aliases] -upload_all = 'sdist bdist_egg bdist_wheel upload' +upload_all = sdist bdist_egg bdist_wheel upload From 0d88a282eea499917bffa64ead0f28fcca2bb2e6 Mon Sep 17 00:00:00 2001 From: Thomas Tanner Date: Sun, 29 Dec 2013 01:56:25 +0100 Subject: [PATCH 49/56] fix import of OrderedDict --- gnupg/_parsers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gnupg/_parsers.py b/gnupg/_parsers.py index 58a681c..dc4d3ae 100644 --- a/gnupg/_parsers.py +++ b/gnupg/_parsers.py @@ -25,9 +25,9 @@ from __future__ import absolute_import from __future__ import print_function try: - import collections + from collections import OrderedDict except ImportError: - import ordereddict as collections + from ordereddict import OrderedDict import re @@ -1027,7 +1027,7 @@ class ImportResult(object): _fields = '''count no_user_id imported imported_rsa unchanged n_uids n_subk n_sigs n_revoc sec_read sec_imported sec_dups not_imported'''.split() - _counts = collections.OrderedDict( + _counts = OrderedDict( zip(_fields, [int(0) for x in range(len(_fields))]) ) #: A list of strings containing the fingerprints of the GnuPG keyIDs From 64fc3bc66f7acf133c0fd3602e7dfa0ed97df100 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 5 Jan 2014 20:00:30 +0000 Subject: [PATCH 50/56] Add 8192-bit-key/* to .gitignore. --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index a8bfc2b..fe5135e 100644 --- a/.gitignore +++ b/.gitignore @@ -86,3 +86,6 @@ MANIFEST # sphinx default build docs/_build + +# ignore keys which have been generated example scripts: +8192-bit-key/* From bc2a21d31c499a70726297d7693bf068eb0d2d27 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Sun, 5 Jan 2014 20:10:23 +0000 Subject: [PATCH 51/56] Add example script which creates an 8192-bit key with 4096-bit subkey. --- examples/make-8192-bit-key.py | 213 ++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100755 examples/make-8192-bit-key.py diff --git a/examples/make-8192-bit-key.py b/examples/make-8192-bit-key.py new file mode 100755 index 0000000..7854f07 --- /dev/null +++ b/examples/make-8192-bit-key.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +"""Create a new 8192-bit GnuPG keypair. + +:authors: Isis 0xa3adb67a2cdb8b35 +:license: MIT license +:copyright: (c) 2013 Isis Agora Lovecruft +""" + +from __future__ import print_function +from __future__ import absolute_import +from __future__ import unicode_literals + +import logging +import gnupg +import sys + +from gnupg import _logger + +# Set up logging: +log = _logger.create_logger(9) +log.setLevel(9) + + +#――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― +# Settings +# +# You probably want to edit the following variables. Ones which are currently +# set to strings are necessary; the ones which are set to `None` are optional. + +# The directory to use as the homedir for GnuPG (it will contain the +# secring.gpg and pubring.gpg, etc.) +NEWKEY_DIR = './8192-bit-key' + +# The name you go by, as it should appear in the primary keyid, i.e. "Evey +# Hammond": +NAME = 'Someone' + +# The comment which goes in parantheses after the name and before the email +# address on the key's primary uid. Leave as None to not have one. +NAME_COMMENT = None + +# The email address for the primary UID (You *should* actually be able to put +# whatever you want here, like a domain or something, because the GnuPG +# `--allow-freeform-uid` option will be used. I've not actually tested this +# though.) +NAME_EMAIL = 'someone@example.com' + +# Expiration date for the new key. To use the default expiration of one year, +# set to None. +#EXPIRE_DATE = '1999-09-19' +EXPIRE_DATE = None + + +# GnuPG-1.4.x allows the automated creation of passphraseless keys. If using +# GnuPG-1.4.x, and you don't specify the passphrase, you can of course set it +# later with `$ gpg --edit-key` and then at the prompt typing `password`. If +# using a GnuPG from the 2.x series, you *must* specify a password here +# (though you can still change it afterward). +PASSPHRASE = None + +# Type of key, i.e. 'RSA' or 'DSA' or something else. I've only tested +# 8192-bit keys with RSA. +KEY_TYPE = 'RSA' + +# Uses for the key. Can be things like 'cert,sign' or 'cert' or 'cert,auth'. +KEY_USAGE = 'cert' + +# Key bitlength. You likely want 8192, if you're using this script. +# +# It *is* possible to create 16834-bit keys, though it requires modifying and +# recompiling GnuPG. Doing this is a bit janky due to internal GnuPG buffers +# in several parts of the codebase being limited to 8192-bits, the key cannot +# be handled by *most* keyservers (there appears to be only one public +# keyserver which supports 16384-bit keys being uploaded to it), and the +# 16834-bit key will likely require the modified GnuPG to work with it (even +# then some operations, such as removal of the primary secret key, but not the +# primary public key, from the keychain will be badly broken). +KEY_LENGTH = 8192 + +# Type of subkey. None to skip subkey generation. You can add keys later +# through `$ gpg --edit-key`. For compatibility with people who aren't doing +# crazy things with their keys, you maybe probably want to use `--edit-key` to +# create some nice, normal, "overly-paranoid" 4096-bit keys. +SUBKEY_TYPE = 'RSA' + +# Same as KEY_USAGE. +#SUBKEY_USAGE = None +SUBKEY_USAGE = 'sign' + +# Same as KEY_LENGTH. +#SUBKEY_LENGTH = None +SUBKEY_LENGTH = 4096 + +# The default keyserver for the key, which is embedded into the key, telling +# other people's GnuPGs to fetch (and send updates) to this URL: +KEYSERVER = None + +# Set the cipher, hash, and compression preference values for this key. This +# expects the same type of string as the sub-command ‘setpref’ in the +# --edit-key menu. The default preferences are given in +# ``gnupg.GPG.default_preference_list``. +PREFERENCES = None +#――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― + + +gpg = gnupg.GPG(homedir=NEWKEY_DIR) +allparams = {'name_real': NAME, + 'name_comment': NAME_COMMENT, + 'name_email': NAME_EMAIL, + 'expire_date': EXPIRE_DATE, + 'passphrase': PASSPHRASE, + 'key_type': KEY_TYPE, + 'key_usage': KEY_USAGE, + 'key_length': KEY_LENGTH, + 'subkey_type': SUBKEY_TYPE, + 'subkey_usage': SUBKEY_USAGE, + 'subkey_length': SUBKEY_LENGTH, + 'keyserver': KEYSERVER, + 'preferences': PREFERENCES} + +def createBatchfile(keyparams=allparams): + """Create the batchfile for our new key. + + :params dict keyparams: A dictionary of arguments for creating the key. It + should probably be ``allparams``. + :rtype: str + :returns: A string containing the entire GnuPG batchfile. + """ + useparams = {} + for key, value in keyparams.items(): + if value: + useparams.update({key: value}) + batchfile = gpg.gen_key_input(separate_keyring=True, + save_batchfile=True, + **useparams) + log.info("Generated GnuPG batch file:\n%s" % batchfile) + return batchfile + +def createKey(batchfile): + """Create a new keypair from a **batchfile**. + + Writes the new keys into keyrings named after ``NAME_EMAIL`` inside the + ``NEWKEY_DIR``. + + :params str batchfile: A GnuPG batchfile. See :func:`createBatchfile`. + """ + key = gpg.gen_key(batchfile) + fingerprint = key.fingerprint + + if not fingerprint: + log.error("Key creation seems to have failed: %s" % key.status) + return None, None + return key, fingerprint + +def displayNewKey(key): + """Use ``gnupg.GPG.list_keys()`` to display details of the new key.""" + + if key.keyring: + gpg.keyring = key.keyring + if key.secring: + gpg.secring = key.secring + + # Using '--fingerprint' twice will display subkey fingerprints too: + gpg.options = ['--fingerprint', '--fingerprint'] + keylist = gpg.list_keys(secret=True) + + # `result` is a `gnupg._parsers.ListKeys`, which is list-like, so iterate + # over all the keys and display their info: + for gpgkey in keylist: + for k, v in gpgkey: + log.info("%s: %s" % (k.capitalize(), v)) + + return keylist + +def exportNewKey(fingerprint): + """Export the new keys into .asc files. + + :param str fingerprint: A full key fingerprint. + """ + log.info("Exporting key: %s" % fingerprint) + + keyfn = os.path.join(gpg.homedir, + fingerprint + '-8192-bit-key') + os.path.extsep + + pubkey = gpg.export_keys(fingerprint) + seckey = gpg.export_keys(fingerprint, secret=True) + subkey = gpg.export_keys(fingerprint, secret=True, subkeys=True) + + with open(keyfn + 'pub' + os.path.extsep + 'asc', 'w') as fh: + fh.write(pubkey) + with open(keyfn + 'sec' + os.path.extsep + 'asc', 'w') as fh: + fh.write(seckey) + with open(keyfn + 'sub' + os.path.extsep + 'asc', 'w') as fh: + fh.write(subkey) + + +if __name__ == '__main__': + if (NAME == 'Someone') or (NAME_EMAIL == 'someone@example.com'): + log.info("Please edit the settings variables within this script.") + log.info("Exiting...") + exit(1) + else: + try: + batchfile = createBatchfile() + key, fingerprint = createKey(batchfile) + log.info("New key with fingerprint %r created" % fingerprint) + displayNewKey(key) + exportNewKey(fingerprint) + + except Exception as error: + log.error(error) From 929fad0a1ae98a2598e865e6cdb03c055582f3a0 Mon Sep 17 00:00:00 2001 From: Matt Behrens Date: Wed, 26 Mar 2014 22:33:03 -0400 Subject: [PATCH 52/56] prune unused exceptions imports for Python 3 --- gnupg/_meta.py | 1 - gnupg/_util.py | 1 - gnupg/gnupg.py | 1 - 3 files changed, 3 deletions(-) diff --git a/gnupg/_meta.py b/gnupg/_meta.py index 6654ac6..7791302 100644 --- a/gnupg/_meta.py +++ b/gnupg/_meta.py @@ -26,7 +26,6 @@ from __future__ import absolute_import import atexit import codecs import encodings -import exceptions ## For AOS, the locale module will need to point to a wrapper around the ## java.util.Locale class. ## See https://code.patternsinthevoid.net/?p=android-locale-hack.git diff --git a/gnupg/_util.py b/gnupg/_util.py index 179ecbf..62d54fe 100644 --- a/gnupg/_util.py +++ b/gnupg/_util.py @@ -27,7 +27,6 @@ from time import mktime import codecs import encodings -import exceptions import os import psutil import threading diff --git a/gnupg/gnupg.py b/gnupg/gnupg.py index 3053d17..f92ca8b 100644 --- a/gnupg/gnupg.py +++ b/gnupg/gnupg.py @@ -31,7 +31,6 @@ from __future__ import absolute_import from codecs import open as open import encodings -import exceptions import functools import os import re From 3be2697a1bbafcee0c0da0cb02bece152134be2a Mon Sep 17 00:00:00 2001 From: Matt Behrens Date: Sun, 30 Mar 2014 19:53:45 -0400 Subject: [PATCH 53/56] WIP fixing serveral Python 3 tests for #16 --- gnupg/_meta.py | 22 +++++++++++----------- gnupg/_parsers.py | 4 ++-- gnupg/_trust.py | 2 +- gnupg/_util.py | 6 +++--- gnupg/gnupg.py | 4 ++-- gnupg/test/test_gnupg.py | 16 ++++++++++------ 6 files changed, 29 insertions(+), 25 deletions(-) diff --git a/gnupg/_meta.py b/gnupg/_meta.py index 7791302..220ed76 100644 --- a/gnupg/_meta.py +++ b/gnupg/_meta.py @@ -153,7 +153,7 @@ class GPGBase(object): self._filesystemencoding = encodings.normalize_encoding( sys.getfilesystemencoding().lower()) - self._keyserver = 'hkp://subkeys.pgp.net' + self._keyserver = 'hkp://wwwkeys.pgp.net' self.__generated_keys = os.path.join(self.homedir, 'generated-keys') try: @@ -164,8 +164,8 @@ class GPGBase(object): if self.options is not None: assert isinstance(self.options, str), "options not string" except (AssertionError, AttributeError) as ae: - log.error("GPGBase.__init__(): %s" % ae.message) - raise RuntimeError(ae.message) + log.error("GPGBase.__init__(): %s" % str(ae)) + raise RuntimeError(str(ae)) else: if verbose is True: # The caller wants logging, but we need a valid --debug-level @@ -200,7 +200,7 @@ class GPGBase(object): try: program = _util._which(prog)[0] except (OSError, IOError, IndexError) as err: - log.err(err.message) + log.err(str(err)) log.err("Cannot find program '%s', not changing PATH." % prog) return @@ -320,14 +320,14 @@ class GPGBase(object): should contain the desired keyserver protocol which is supported by the keyserver, for example, ``'hkps://keys.mayfirst.org'``. The default - keyserver is ``'hkp://subkeys.pgp.net'``. + keyserver is ``'hkp://wwwkeys.pgp.net'``. """ self._keyserver = location @keyserver.deleter def keyserver(self): """Reset the keyserver to the default setting.""" - self._keyserver = 'hkp://subkeys.pgp.net' + self._keyserver = 'hkp://wwwkeys.pgp.net' def _homedir_getter(self): """Get the directory currently being used as GnuPG's homedir. @@ -373,8 +373,8 @@ class GPGBase(object): except AssertionError as ae: msg = ("Unable to set '%s' as GnuPG homedir" % directory) log.debug("GPGBase.homedir.setter(): %s" % msg) - log.debug(ae.message) - raise RuntimeError(ae.message) + log.debug(str(ae)) + raise RuntimeError(str(ae)) else: log.info("Setting homedir to '%s'" % hd) self._homedir = hd @@ -425,8 +425,8 @@ class GPGBase(object): except AssertionError as ae: msg = ("Unable to set '%s' as generated keys dir" % directory) log.debug("GPGBase._generated_keys_setter(): %s" % msg) - log.debug(ae.message) - raise RuntimeError(ae.message) + log.debug(str(ae)) + raise RuntimeError(str(ae)) else: log.info("Setting homedir to '%s'" % hd) self.__generated_keys = hd @@ -696,7 +696,7 @@ class GPGBase(object): _util._write_passphrase(proc.stdin, passphrase, self._encoding) writer = _util._threaded_copy_data(file, proc.stdin) except IOError as ioe: - log.exception("Error writing message: %s" % ioe.message) + log.exception("Error writing message: %s" % str(ioe)) writer = None self._collect_output(proc, result, writer, proc.stdin) return result diff --git a/gnupg/_parsers.py b/gnupg/_parsers.py index 58a681c..93d9a86 100644 --- a/gnupg/_parsers.py +++ b/gnupg/_parsers.py @@ -53,7 +53,7 @@ def _check_keyserver(location): :param str location: A string containing the default keyserver. This should contain the desired keyserver protocol which is supported by the keyserver, for example, the - default is ``'hkp://subkeys.pgp.net'``. + default is ``'hkp://wwwkeys .pgp.net'``. :rtype: :obj:`str` or :obj:`None` :returns: A string specifying the protocol and keyserver hostname, if the checks passed. If not, returns None. @@ -301,7 +301,7 @@ def _sanitise(*args): flag = _is_allowed(arg) assert flag is not None, "_check_option(): got None for flag" except (AssertionError, ProtectedOption) as error: - log.warn("_check_option(): %s" % error.message) + log.warn("_check_option(): %s" % str(error)) else: checked += (flag + ' ') diff --git a/gnupg/_trust.py b/gnupg/_trust.py index 62d8fc6..514ae8c 100644 --- a/gnupg/_trust.py +++ b/gnupg/_trust.py @@ -55,7 +55,7 @@ def export_ownertrust(cls, trustdb=None): try: os.rename(trustdb, trustdb + '.bak') except (OSError, IOError) as err: - log.debug(err.message) + log.debug(str(err)) export_proc = cls._open_subprocess('--export-ownertrust') tdb = open(trustdb, 'wb') diff --git a/gnupg/_util.py b/gnupg/_util.py index 62d54fe..e1e14ab 100644 --- a/gnupg/_util.py +++ b/gnupg/_util.py @@ -148,7 +148,7 @@ def _copy_data(instream, outstream): break except IOError as ioe: # Can get 'broken pipe' errors even when all data was sent - if 'Broken pipe' in ioe.message: + if 'Broken pipe' in str(ioe): log.error('Error sending data: Broken pipe') else: log.exception(ioe) @@ -286,7 +286,7 @@ def _find_binary(binary=None): assert not os.path.islink(found), "Path to gpg binary is symlink" assert os.access(found, os.X_OK), "Lacking +x perms for gpg binary" except (AssertionError, AttributeError) as ae: - log.error(ae.message) + log.error(str(ae)) else: return found @@ -604,7 +604,7 @@ class Storage(dict): try: del self[key] except KeyError as k: - raise AttributeError(k.message) + raise AttributeError(k.args[0]) def __repr__(self): return '' diff --git a/gnupg/gnupg.py b/gnupg/gnupg.py index f92ca8b..29bedaa 100644 --- a/gnupg/gnupg.py +++ b/gnupg/gnupg.py @@ -571,13 +571,13 @@ class GPG(GPGBase): if os.path.isfile(self.temp_keyring): prefix = os.path.join(self.temp_keyring, fpr) try: os.rename(self.temp_keyring, prefix+".pubring") - except OSError as ose: log.error(ose.message) + except OSError as ose: log.error(str(ose)) if self.temp_secring: if os.path.isfile(self.temp_secring): prefix = os.path.join(self.temp_secring, fpr) try: os.rename(self.temp_secring, prefix+".secring") - except OSError as ose: log.error(ose.message) + except OSError as ose: log.error(str(ose)) log.info("Key created. Fingerprint: %s" % fpr) key.keyring = self.temp_keyring diff --git a/gnupg/test/test_gnupg.py b/gnupg/test/test_gnupg.py index 1f3b37a..41b9d87 100644 --- a/gnupg/test/test_gnupg.py +++ b/gnupg/test/test_gnupg.py @@ -62,7 +62,7 @@ try: import gnupg._parsers as _parsers import gnupg._logger as _logger except (ImportError, ValueError) as ierr: - raise SystemExit(ierr.message) + raise SystemExit(str(ierr)) log = _util.log @@ -325,15 +325,19 @@ class GPGTestCase(unittest.TestCase): def test_copy_data_bytesio(self): """Test that _copy_data() is able to duplicate byte streams.""" - message = "This is a BytesIO string." + message = b"This is a BytesIO string." instream = io.BytesIO(message) - self.assertEqual(unicode(message), instream.getvalue()) + self.assertEqual(message, instream.getvalue()) out_filename = 'test-copy-data-bytesio' # Create the test file: - outfile = os.path.join(os.getcwdu(), out_filename) - outstream = open(outfile, 'w+') + try: + cwd = os.getcwdu() + except AttributeError: + cwd = os.getcwd() # not present in Python 3 + outfile = os.path.join(cwd, out_filename) + outstream = open(outfile, 'wb+') # _copy_data() will close both file descriptors _util._copy_data(instream, outstream) @@ -685,7 +689,7 @@ class GPGTestCase(unittest.TestCase): self.assertTrue(sig.data, "File signing should succeed") - sigfd = open(sigfn, 'w') + sigfd = open(sigfn, 'wb') sigfd.write(sig.data) sigfd.flush() From e87e67cf15339a22f933693f0acf80d7950800d8 Mon Sep 17 00:00:00 2001 From: Matt Behrens Date: Mon, 31 Mar 2014 11:32:35 -0400 Subject: [PATCH 54/56] more tests for #16. all now pass except one --- gnupg/test/test_gnupg.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/gnupg/test/test_gnupg.py b/gnupg/test/test_gnupg.py index 41b9d87..9aebf69 100644 --- a/gnupg/test/test_gnupg.py +++ b/gnupg/test/test_gnupg.py @@ -929,15 +929,15 @@ analysis of different kinds of data (temperature, humidity, etc.) coming from a WSN while ensuring both end-to-end encryption and hop-by-hop authentication.""" enc = self.gpg.encrypt(message, alice_pfpr, bob_pfpr) - encrypted = str(enc.data) + encrypted = str(enc) log.debug("encryption_decryption_multi_recipient() Ciphertext = %s" % encrypted) self.assertNotEquals(message, encrypted) dec_alice = self.gpg.decrypt(encrypted, passphrase="test") - self.assertEquals(message, str(dec_alice.data)) + self.assertEquals(message, str(dec_alice)) dec_bob = self.gpg.decrypt(encrypted, passphrase="test") - self.assertEquals(message, str(dec_bob.data)) + self.assertEquals(message, str(dec_bob)) def test_symmetric_encryption_and_decryption(self): """Test symmetric encryption and decryption""" @@ -947,7 +947,7 @@ know, maybe you shouldn't be doing it in the first place. encrypted = str(self.gpg.encrypt(msg, passphrase='quiscustodiet', symmetric=True, encrypt=False)) decrypt = self.gpg.decrypt(encrypted, passphrase='quiscustodiet') - decrypted = str(decrypt.data) + decrypted = str(decrypt) log.info("Symmetrically encrypted data:\n%s" % encrypted) log.info("Symmetrically decrypted data:\n%s" % decrypted) @@ -979,9 +979,8 @@ know, maybe you shouldn't be doing it in the first place. with open(enc_outf) as enc2: fdata = enc2.read() - ddata = str(self.gpg.decrypt(fdata, passphrase="overalls")) + ddata = self.gpg.decrypt(fdata, passphrase="overalls").data - data = data.encode(self.gpg._encoding) if ddata != data: log.debug("data was: %r" % data) log.debug("new (from filehandle): %r" % fdata) From 6b7fe3467d71c5712935cae2160c36d2adc104f6 Mon Sep 17 00:00:00 2001 From: Thomas Tanner Date: Wed, 23 Apr 2014 14:06:30 +0200 Subject: [PATCH 55/56] py2.6 support for testing --- gnupg/test/test_gnupg.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gnupg/test/test_gnupg.py b/gnupg/test/test_gnupg.py index 1f3b37a..de14572 100644 --- a/gnupg/test/test_gnupg.py +++ b/gnupg/test/test_gnupg.py @@ -47,8 +47,8 @@ import tempfile ## these dependencies require Python>=2.6 in order to have proper SSL support. ## ## Use unittest2 if we're on Python2.6 or less: -if sys.version_info.major == 2 and sys.version_info.minor <= 6: - unittest = __import__(unittest2) +if sys.version_info[0] == 2 and sys.version_info[1] <= 6: + import unittest2 as unittest else: import unittest From 1fd400b5d3517e66ea81d8119cacdf06e18bcf2a Mon Sep 17 00:00:00 2001 From: Thomas Tanner Date: Wed, 23 Apr 2014 14:49:24 +0200 Subject: [PATCH 56/56] more py2.6 fixes --- gnupg/_logger.py | 6 +++--- gnupg/_meta.py | 4 ++-- gnupg/gnupg.py | 4 ++-- gnupg/test/test_gnupg.py | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) mode change 100644 => 100755 gnupg/test/test_gnupg.py diff --git a/gnupg/_logger.py b/gnupg/_logger.py index 6dd93c7..870617e 100644 --- a/gnupg/_logger.py +++ b/gnupg/_logger.py @@ -78,13 +78,13 @@ def create_logger(level=logging.NOTSET): if level > logging.NOTSET: logging.basicConfig(level=level, filename=_fn, filemode="a", format=_fmt) - logging.captureWarnings(True) logging.logThreads = True - + if hasattr(logging,'captureWarnings'): + logging.captureWarnings(True) colouriser = _ansistrm.ColorizingStreamHandler colouriser.level_map[9] = (None, 'blue', False) colouriser.level_map[10] = (None, 'cyan', False) - handler = colouriser(stream=sys.stderr) + handler = colouriser(sys.stderr) handler.setLevel(level) formatr = logging.Formatter(_fmt) diff --git a/gnupg/_meta.py b/gnupg/_meta.py index 6654ac6..fb7fea8 100644 --- a/gnupg/_meta.py +++ b/gnupg/_meta.py @@ -640,8 +640,8 @@ class GPGBase(object): if not keyserver: keyserver = self.keyserver - args = ['--keyserver {}'.format(keyserver), - '--recv-keys {}'.format(keyids)] + args = ['--keyserver {0}'.format(keyserver), + '--recv-keys {0}'.format(keyids)] log.info('Requesting keys from %s: %s' % (keyserver, keyids)) result = self._result_map['import'](self) diff --git a/gnupg/gnupg.py b/gnupg/gnupg.py index 3053d17..711a27f 100644 --- a/gnupg/gnupg.py +++ b/gnupg/gnupg.py @@ -416,7 +416,7 @@ class GPG(GPGBase): fingerprints = ' '.join(fingerprints) args = ['--batch'] - args.append("--delete-{} {}".format(which, fingerprints)) + args.append("--delete-{0} {1}".format(which, fingerprints)) result = self._result_map['delete'](self) p = self._open_subprocess(args) @@ -441,7 +441,7 @@ class GPG(GPGBase): keyids = ' '.join(['%s' % k for k in keyids]) args = ["--armor"] - args.append("--export{} {}".format(which, keyids)) + args.append("--export{0} {1}".format(which, keyids)) p = self._open_subprocess(args) ## gpg --export produces no status-fd output; stdout will be empty in diff --git a/gnupg/test/test_gnupg.py b/gnupg/test/test_gnupg.py old mode 100644 new mode 100755 index de14572..2dec27b --- a/gnupg/test/test_gnupg.py +++ b/gnupg/test/test_gnupg.py @@ -820,14 +820,14 @@ authentication.""" riggio_input = self.gpg.gen_key_input(separate_keyring=True, **riggio) log.info("Key stored in separate keyring: %s" % self.gpg.temp_keyring) riggio = self.gpg.gen_key(riggio_input) - self.gpg.options = ['--keyring {}'.format(riggio.keyring)] + self.gpg.options = ['--keyring {0}'.format(riggio.keyring)] riggio_key = self.gpg.export_keys(riggio.fingerprint) self.gpg.import_keys(riggio_key) sicari_input = self.gpg.gen_key_input(separate_keyring=True, **sicari) log.info("Key stored in separate keyring: %s" % self.gpg.temp_keyring) sicari = self.gpg.gen_key(sicari_input) - self.gpg.options.append('--keyring {}'.format(sicari.keyring)) + self.gpg.options.append('--keyring {0}'.format(sicari.keyring)) sicari_key = self.gpg.export_keys(sicari.fingerprint) self.gpg.import_keys(sicari_key)