Merge branch 'release/1.2.5'

fix/30-hidden-recipient
Isis Lovecruft 2013-12-04 11:45:46 +00:00
commit 3cdfbc2f9b
No known key found for this signature in database
GPG Key ID: 5C17776E27F7E84D
20 changed files with 490 additions and 998 deletions

3
.gitignore vendored
View File

@ -83,3 +83,6 @@ gpg
# setuptools/distribute files:
PKG-INFO
MANIFEST
# sphinx default build
docs/_build

View File

@ -2,7 +2,7 @@
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXOPTS = -E -n
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build

View File

@ -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 {

View File

@ -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() {
$('<a class="headerlink">\u00B6</a>').
attr('href', '#' + this.id).
attr('title', _('Permalink to this headline')).
appendTo(this);
});
$('dt[id]').each(function() {
$('<a class="headerlink">\u00B6</a>').
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);
$('<li class="highlight-link"><a href="javascript:Documentation.' +
'hideSearchWords()">' + _('Hide Search Matches') + '</a></li>')
.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();
});

386
docs/_static/haiku.css vendored
View File

@ -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;
}

File diff suppressed because one or more lines are too long

View File

@ -29,11 +29,16 @@ 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.
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']
@ -42,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'
@ -83,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.
@ -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 ---------------------------------------------------

View File

@ -17,6 +17,7 @@ do:
:private-members:
:show-inheritance:
.. _meta:
meta module
-----------
@ -28,10 +29,13 @@ 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:
parsers module
--------------
@ -39,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
@ -52,6 +57,8 @@ source_, which has been included here_ as well.
:show-inheritance:
.. _util:
util module
-----------
@ -75,51 +82,50 @@ 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
-------------------------------
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
.. _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

View File

@ -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.

View File

@ -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

View File

@ -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
@ -51,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")
@ -86,10 +89,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)

View File

@ -7,20 +7,18 @@
# © 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.
'''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
@ -28,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
@ -52,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):
@ -69,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,
@ -87,8 +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,
@ -103,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'
@ -154,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 = []
@ -222,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.
@ -270,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.
@ -324,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'"
@ -365,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')
@ -407,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
@ -489,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:
@ -535,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
<parsers>` from :const:`~gnupg._meta.GPGBase._result_map`.
"""
chunks = []
log.debug("Reading data from stream %r..." % stream.__repr__())
@ -604,7 +635,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
@ -631,9 +662,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:
@ -681,46 +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 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.
be specified keyID/fingerprint.
.. 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 str default_key: The keyID/fingerprint of the key to use for
signing. If given, ``data`` will be encrypted and signed.
signing. If given, **data** will be encrypted
*and* signed.
: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 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 bool armor: If True, ascii armor the output; otherwise, the
output will be in binary format. (Default: True)
output will be in binary format. (Default: True)
:param bool encrypt: If True, encrypt the ``data`` using the
``recipients`` public keys. (Default: True)
:param bool encrypt: If True, encrypt the **data** using the
**recipients** public keys. (Default: True)
: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 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 recipient
keys. If False, display trust warnings. (default: True)
: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:
encrypted output is returned, and thus should be
stored as an object in Python. For example:
>>> import shutil
>>> import gnupg
@ -742,17 +783,20 @@ 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:
: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``.
The default, if unspecified, is ``'SHA512'``.
hashes your GnuPG is capable of using, do:
: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'``.
of ``'ZLIB'``, ``'BZIP2'``, ``'ZIP'``, or
``'Uncompressed'``.
"""
args = []

View File

@ -7,25 +7,28 @@
# © 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.
'''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
from __future__ import print_function
import collections
try:
import collections
except ImportError:
import ordereddict as collections
import re
from . import _util
@ -51,7 +54,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.
"""
@ -74,10 +77,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
@ -115,8 +119,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.
"""
@ -161,11 +165,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")
@ -220,12 +228,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)
@ -238,18 +246,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.
@ -260,22 +270,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")
@ -332,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))
@ -574,21 +586,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``
"""
@ -791,8 +804,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
@ -803,11 +816,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):
@ -827,7 +842,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
@ -864,7 +879,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"
@ -909,7 +924,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",
@ -1041,7 +1056,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
@ -1187,7 +1202,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
@ -1282,7 +1297,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",
@ -1349,7 +1364,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)

View File

@ -7,19 +7,17 @@
# © 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.
'''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.
@ -44,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')
@ -65,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,22 +78,23 @@ 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:
instead::
(python-gnupg)!isiswintermute:(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)~/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')

View File

@ -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
@ -30,6 +27,7 @@ from time import mktime
import codecs
import encodings
import exceptions
import os
import psutil
import threading
@ -255,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.
@ -304,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.
@ -519,7 +527,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

View File

@ -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.
@ -21,16 +21,17 @@
===========
A Python interface to GnuPG.
.. moduleauthor:: Isis Agora Lovecruft <isis@patternsinthevoid.net>
.. moduleauthor:: Isis Lovecruft <isis@patternsinthevoid.net>
see also :attr:`gnupg.__authors__`
:license: see :attr:`gnupg.__license__`
:info: see <https://www.github.com/isislovecruft/python-gnupg>
.. 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():
@ -306,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):
@ -373,7 +379,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])
@ -384,27 +390,27 @@ 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`.
:type fingerprints: str or list or tuple
be deleted from :obj:`.secring`.
: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.
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:
:command:`$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 +431,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 +447,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 +473,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]
@ -547,7 +553,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 <GenKey>` object.
:class:`GenKey <gnupg._parsers.GenKey>` object.
"""
args = ["--gen-key --batch"]
key = self._result_map['generate'](self)
@ -584,11 +590,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
@ -647,9 +653,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 <name_real>.batch, where <name_real> is the
@ -663,11 +670,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
@ -706,7 +714,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
<int>[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)
@ -728,17 +736,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
@ -749,18 +757,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
@ -933,20 +941,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)

View File

@ -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."""

View File

@ -7,3 +7,6 @@ verbose = true
sign = True
identity = 0xa3adb67a2cdb8b35
[aliases]
upload_all = sdist bdist_egg bdist_wheel upload

View File

@ -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,15 +71,14 @@ 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
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(
@ -105,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",