Merge branch 'feature/cleanup-api' into develop

testing/mmn/mktime_takes_localtime_not_gmtime
Isis Lovecruft 2013-04-17 23:52:10 +00:00
commit fd6dbe71b6
No known key found for this signature in database
GPG Key ID: A3ADB67A2CDB8B35
5 changed files with 251 additions and 181 deletions

View File

@ -5,13 +5,13 @@ clean:
rm -f ./*.pyo
cleantest: clean
mkdir -p gnupg/tests/keys
mkdir -p gnupg/tests/tmp_test
touch gnupg/tests/placeholder.log
rm -rf gnupg/tests/keys
rm -rf gnupg/tests/tmp_test
rm gnupg/tests/*.log
test: cleantest
python gnupg/tests/test_gnupg.py parsers basic genkey sign
python gnupg/tests/test_gnupg.py parsers basic genkey sign listkeys
install:
python setup.py install --record installed-files.txt

View File

@ -107,78 +107,7 @@ from parsers import GenKey, Sign, ListKeys, ListPackets
from parsers import _fix_unsafe, _sanitise, _is_allowed, _sanitise_list
from util import logger, _conf
import util
def _copy_data(instream, outstream):
"""Copy data from one stream to another.
:type instream: :class:`io.BytesIO` or :class:`io.StringIO` or file
:param instream: A byte stream or open file to read from.
:param file outstream: The file descriptor of a tmpfile to write to.
"""
sent = 0
try:
#assert (util._is_stream(instream)
# or isinstance(instream, file)), "instream not stream or file"
assert isinstance(outstream, file), "outstream is not a file"
except AssertionError as ae:
logger.exception(ae)
return
if hasattr(sys.stdin, 'encoding'):
enc = sys.stdin.encoding
else:
enc = 'ascii'
while True:
data = instream.read(1024)
if len(data) == 0:
break
sent += len(data)
logger.debug("_copy_data(): sending chunk (%d):\n%s" % (sent, data[:256]))
try:
outstream.write(data)
except UnicodeError:
try:
outstream.write(data.encode(enc))
except IOError:
logger.exception('_copy_data(): Error sending data: Broken pipe')
break
except IOError:
# Can get 'broken pipe' errors even when all data was sent
logger.exception('_copy_data(): Error sending data: Broken pipe')
break
try:
outstream.close()
except IOError:
logger.exception('_copy_data(): Got IOError while closing %s' % outstream)
else:
logger.debug("_copy_data(): Closed output, %d bytes sent." % sent)
def _threaded_copy_data(instream, outstream):
"""Copy data from one stream to another in a separate thread.
Wraps ``_copy_data()`` in a :class:`threading.Thread`.
:type instream: :class:`io.BytesIO` or :class:`io.StringIO`
:param instream: A byte stream to read from.
:param file outstream: The file descriptor of a tmpfile to write to.
"""
copy_thread = threading.Thread(target=_copy_data,
args=(instream, outstream))
copy_thread.setDaemon(True)
logger.debug('_threaded_copy_data(): %r, %r, %r', copy_thread,
instream, outstream)
copy_thread.start()
return copy_thread
def _write_passphrase(stream, passphrase, encoding):
passphrase = '%s\n' % passphrase
passphrase = passphrase.encode(encoding)
stream.write(passphrase)
logger.debug("_write_passphrase(): Wrote passphrase.")
import util as _util
class GPG(object):
@ -227,12 +156,12 @@ class GPG(object):
gpghome = _conf
self.gpghome = _fix_unsafe(gpghome)
if self.gpghome:
util._create_gpghome(self.gpghome)
_util._create_gpghome(self.gpghome)
else:
message = ("Unsuitable gpg home dir: %s" % gpghome)
logger.debug("GPG.__init__(): %s" % message)
self.gpgbinary = util._find_gpgbinary(gpgbinary)
self.gpgbinary = _util._find_gpgbinary(gpgbinary)
if keyring is not None:
raise DeprecationWarning("Option 'keyring' changing to 'secring'")
@ -242,17 +171,6 @@ class GPG(object):
self.secring = os.path.join(self.gpghome, secring)
self.pubring = os.path.join(self.gpghome, pubring)
#for ring in [self.secring, self.pubring]:
# if ring and not os.path.isfile(ring):
# with open(ring, 'a+') as ringfile:
# ringfile.write("")
# ringfile.flush()
# try:
# assert util._has_readwrite(ring), \
# ("Need r+w for %s" % ring)
# except AssertionError as ae:
# logger.debug(ae.message)
self.options = _sanitise(options) if options else None
self.encoding = locale.getpreferredencoding()
@ -261,7 +179,7 @@ class GPG(object):
try:
assert self.gpghome is not None, "Got None for self.gpghome"
assert util._has_readwrite(self.gpghome), ("Home dir %s needs r+w"
assert _util._has_readwrite(self.gpghome), ("Home dir %s needs r+w"
% self.gpghome)
assert self.gpgbinary, "Could not find gpgbinary %s" % full
assert isinstance(verbose, bool), "'verbose' must be boolean"
@ -354,7 +272,7 @@ class GPG(object):
break
logger.debug("chunk: %r" % data[:256])
chunks.append(data)
if util._py3k:
if _util._py3k:
# Join using b'' or '', as appropriate
result.data = type(data)().join(chunks)
else:
@ -399,8 +317,8 @@ class GPG(object):
else:
stdin = p.stdin
if passphrase:
_write_passphrase(stdin, passphrase, self.encoding)
writer = _threaded_copy_data(file, stdin)
_util._write_passphrase(stdin, passphrase, self.encoding)
writer = _util._threaded_copy_data(file, stdin)
self._collect_output(p, result, writer, stdin)
return result
@ -411,8 +329,8 @@ class GPG(object):
"""Create a signature for a message or file."""
if isinstance(message, file):
result = self._sign_file(message, **kwargs)
elif not util._is_stream(message):
f = util._make_binary_stream(message, self.encoding)
elif not _util._is_stream(message):
f = _util._make_binary_stream(message, self.encoding)
result = self._sign_file(f, **kwargs)
f.close()
else:
@ -448,8 +366,8 @@ class GPG(object):
try:
stdin = p.stdin
if passphrase:
_write_passphrase(stdin, passphrase, self.encoding)
writer = _threaded_copy_data(file, stdin)
_util._write_passphrase(stdin, passphrase, self.encoding)
writer = _util._threaded_copy_data(file, stdin)
except IOError:
logger.exception("_sign_file(): Error writing message")
writer = None
@ -471,7 +389,7 @@ class GPG(object):
>>> assert verify
"""
f = util._make_binary_stream(data, self.encoding)
f = _util._make_binary_stream(data, self.encoding)
result = self.verify_file(f)
f.close()
return result
@ -483,7 +401,7 @@ class GPG(object):
detached signature should be specified as the ``sig_file``.
:param file file: A file descriptor object. Its type will be checked
with :func:`util._is_file`.
with :func:`_util._is_file`.
:param str sig_file: A file containing the GPG signature data for
``file``. If given, ``file`` is verified via this
detached signature.
@ -496,10 +414,10 @@ class GPG(object):
logger.debug("verify_file(): Handling embedded signature")
args = ["--verify"]
proc = self._open_subprocess(args)
writer = _threaded_copy_data(file, proc.stdin)
writer = _util._threaded_copy_data(file, proc.stdin)
self._collect_output(proc, result, writer, stdin=proc.stdin)
else:
if not util._is_file(sig_file):
if not _util._is_file(sig_file):
logger.debug("verify_file(): '%r' is not a file" % sig_file)
return result
logger.debug('verify_file(): Handling detached verification')
@ -508,7 +426,7 @@ class GPG(object):
sig_fh = open(sig_file)
args = ["--verify %s - " % sig_fh.name]
proc = self._open_subprocess(args)
writer = _threaded_copy_data(file, proc.stdin)
writer = _util._threaded_copy_data(file, proc.stdin)
self._collect_output(proc, result, stdin=proc.stdin)
finally:
if sig_fh and not sig_fh.closed:
@ -568,7 +486,7 @@ class GPG(object):
result = self._result_map['import'](self)
logger.debug('import_keys: %r', key_data[:256])
data = util._make_binary_stream(key_data, self.encoding)
data = _util._make_binary_stream(key_data, self.encoding)
self._handle_io(['--import'], data, result, binary=True)
logger.debug('import_keys result: %r', result.__dict__)
data.close()
@ -587,7 +505,7 @@ class GPG(object):
safe_keyserver = _fix_unsafe(keyserver)
result = self._result_map['import'](self)
data = util._make_binary_stream("", self.encoding)
data = _util._make_binary_stream("", self.encoding)
args = ['--keyserver', keyserver, '--recv-keys']
if keyids:
@ -606,7 +524,7 @@ class GPG(object):
which='key'
if secret:
which='secret-key'
if util._is_list_or_tuple(fingerprints):
if _util._is_list_or_tuple(fingerprints):
fingerprints = ' '.join(fingerprints)
args = ['--batch --delete-%s "%s"' % (which, fingerprints)]
result = self._result_map['delete'](self)
@ -619,7 +537,7 @@ class GPG(object):
which=''
if secret:
which='-secret-key'
if util._is_list_or_tuple(keyids):
if _util._is_list_or_tuple(keyids):
keyids = ' '.join(['"%s"' % k for k in keyids])
args = ["--armor --export%s %s" % (which, keyids)]
p = self._open_subprocess(args)
@ -650,7 +568,8 @@ class GPG(object):
which='public-keys'
if secret:
which='secret-keys'
args = "--list-%s --fixed-list-mode --fingerprint --with-colons --no-show-photos" % (which,)
args = "--list-%s --fixed-list-mode --fingerprint " % (which,)
args += "--with-colons --list-options no-show-photos"
args = [args]
p = self._open_subprocess(args)
@ -689,9 +608,8 @@ class GPG(object):
raise NotImplemented("Functionality for '--list-sigs' not implemented.")
def gen_key(self, input):
"""
Generate a key; you might use gen_key_input() to create the control
input.
"""Generate a GnuPG key through batch file key generation. See
:meth:`GPG.gen_key_input()` for creating the control input.
>>> gpg = GPG(gpghome="keys")
>>> input = gpg.gen_key_input()
@ -700,24 +618,77 @@ class GPG(object):
>>> result = gpg.gen_key('foo')
>>> assert not result
: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.
"""
args = ["--gen-key --batch"]
key = self._result_map['generate'](self)
f = util._make_binary_stream(input, self.encoding)
f = _util._make_binary_stream(input, self.encoding)
self._handle_io(args, f, key, binary=True)
f.close()
return key
def gen_key_input(self, **kwargs):
"""Generate GnuPG key(s) through batch file key generation.
"""Generate a batch file for input to :meth:`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 file>
to: ``gpg --gen-key --batch``:
Key-Type: RSA
Key-Length: 4096
Name-Real: Autogenerated Key
Name-Email: %s@%s
Expire-Date: 2014-04-01
%pubring foo.gpg
%secring sec.gpg
%commit
Key-Type: DSA
Key-Length: 1024
Subkey-Type: ELG-E
Subkey-Length: 1024
Name-Real: Joe Tester
Name-Comment: with stupid passphrase
Name-Email: joe@foo.bar
Expire-Date: 0
Passphrase: abc
%pubring foo.pub
%secring foo.sec
%commit
see http://www.gnupg.org/documentation/manuals/gnupg-devel/Unattended-GPG-key-generation.html#Unattended-GPG-key-generation
for more details.
>>> gpg = GPG(gpghome="keys")
>>> params = {'name_real':'python-gnupg tester', 'name_email':'test@ing'}
>>> key_input = gpg.gen_key_input(**params)
>>> result = gpg.gen_key(input)
>>> assert result
:param str name_real: The uid name for the generated key.
:param str name_email: The uid email for the generated key. (default:
$USERNAME@$HOSTNAME)
:param str name_comment: The comment in the uid of the generated key.
:param str key_type: One of 'RSA', 'DSA', or 'ELG-E'. (default: 'RSA')
:param int key_length: The length in bytes of the new key.
(default: 4096)
:param str subkey_type: If ``key_type`` is 'RSA', an additional subkey
can be generated, and it's type must also be 'RSA'. If ``key_type``
is 'DSA', then the only subkey type which can be generated is
'ELG-E'.
:param int subkey_length: The length in bytes of the new subkey.
:type expire: int or str
:param expire: If an integer, the number of days before the key will
expire; if 0, the key will not expire. Otherwise, this can be given
as a string in the form <n>w or <n>m or <n>y, i.e. "5m" would mean
that the key will expire in five months, "1w" would expire in one
week, and "3y" would expire in three years. (default: "1y")
:param str passphrase: The passphrase for the new key.
"""
parms = {}
for key, val in list(kwargs.items()):
key = key.replace('_','-').title()
@ -726,7 +697,7 @@ class GPG(object):
parms.setdefault('Key-Type', 'RSA')
parms.setdefault('Key-Length', 4096)
parms.setdefault('Name-Real', "Autogenerated Key")
parms.setdefault('Name-Comment', "Generated by python-gnupg")
parms.setdefault('Expire-Date', _util._next_year())
try:
logname = os.environ['LOGNAME']
except KeyError:
@ -734,6 +705,7 @@ class GPG(object):
hostname = socket.gethostname()
parms.setdefault('Name-Email', "%s@%s"
% (logname.replace(' ', '_'), hostname))
out = "Key-Type: %s\n" % parms.pop('Key-Type')
for key, val in list(parms.items()):
out += "%s: %s\n" % (key, val)
@ -742,28 +714,6 @@ class GPG(object):
out += "%commit\n"
return out
# Key-Type: RSA
# Key-Length: 1024
# Name-Real: ISdlink Server on %s
# Name-Comment: Created by %s
# Name-Email: isdlink@%s
# Expire-Date: 0
# %commit
#
#
# Key-Type: DSA
# Key-Length: 1024
# Subkey-Type: ELG-E
# Subkey-Length: 1024
# Name-Real: Joe Tester
# Name-Comment: with stupid passphrase
# Name-Email: joe@foo.bar
# Expire-Date: 0
# Passphrase: abc
# %pubring foo.pub
# %secring foo.sec
# %commit
#
# ENCRYPTION
#
@ -805,7 +755,7 @@ class GPG(object):
args = ['--symmetric']
else:
args = ['--encrypt']
if not util._is_list_or_tuple(recipients):
if not _util._is_list_or_tuple(recipients):
recipients = (recipients,)
for recipient in recipients:
args.append('--recipient "%s"' % recipient)
@ -861,7 +811,7 @@ class GPG(object):
>>> assert result.fingerprint == print1
"""
data = util._make_binary_stream(data, self.encoding)
data = _util._make_binary_stream(data, self.encoding)
result = self.encrypt_file(data, recipients, **kwargs)
data.close()
return result
@ -871,7 +821,7 @@ class GPG(object):
:param message: A string or file-like object to decrypt.
"""
data = util._make_binary_stream(message, self.encoding)
data = _util._make_binary_stream(message, self.encoding)
result = self.decrypt_file(data, **kwargs)
data.close()
return result
@ -965,7 +915,7 @@ class GPGWrapper(GPG):
"""Send keys to a keyserver."""
result = self._result_map['list'](self)
gnupg.logger.debug('send_keys: %r', keyids)
data = gnupg.util._make_binary_stream("", self.encoding)
data = gnupg._util._make_binary_stream("", self.encoding)
args = ['--keyserver', keyserver, '--send-keys']
args.extend(keyids)
self._handle_io(args, data, result, binary=True)
@ -985,7 +935,7 @@ class GPGWrapper(GPG):
args.append('--cipher-algo %s' % cipher_algo)
else:
args = ['--encrypt']
if not util._is_list_or_tuple(recipients):
if not _util._is_list_or_tuple(recipients):
recipients = (recipients,)
for recipient in recipients:
args.append('--recipient "%s"' % recipient)
@ -1008,7 +958,7 @@ class GPGWrapper(GPG):
args = ["--list-packets"]
result = self._result_map['list-packets'](self)
self._handle_io(args,
util._make_binary_stream(raw_data, self.encoding),
_util._make_binary_stream(raw_data, self.encoding),
result)
return result

View File

@ -275,7 +275,7 @@ def _is_allowed(input):
['--list-keys', '--list-key', '--fixed-list-mode',
'--list-secret-keys', '--list-public-keys',
'--list-packets', '--with-colons',
'--no-show-photos',
'--list-options',
'--delete-keys', '--delete-secret-keys',
'--encrypt', '--encrypt-files',
'--decrypt', '--decrypt-files',

View File

@ -11,7 +11,6 @@ import argparse
import doctest
import logging
from functools import wraps
import inspect
import io
import os
import shutil
@ -30,24 +29,24 @@ from gnupg import parsers
from gnupg import util
__author__ = gnupg.__author__
__date__ = gnupg.__date__
__version__ = gnupg.__version__
REPO_DIR = os.getcwd()
HOME_DIR = os.path.join(REPO_DIR, 'keys')
tempfile.tempdir = os.path.join(REPO_DIR, 'tmp_test')
logger = logging.getLogger('gnupg')
_here = os.path.join(os.path.join(util._repo, 'gnupg'), 'tests')
_files = os.path.join(_here, 'files')
tempfile.tempdir = os.path.join(_here, 'tmp_test')
if not os.path.isdir(tempfile.gettempdir()):
os.mkdir(tempfile.gettempdir())
HOME_DIR = tempfile.tempdir
@wraps(tempfile.TemporaryFile)
def _make_tempfile(*args, **kwargs):
return tempfile.TemporaryFile(dir=tempfile.gettempdir(),
*args, **kwargs)
logger = logging.getLogger('gnupg')
_here = os.path.join(os.path.join(util._repo, 'gnupg'), 'tests')
_files = os.path.join(_here, 'files')
KEYS_TO_IMPORT = """-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.9 (MingW32)
@ -118,11 +117,10 @@ def compare_keys(k1, k2):
class ResultStringIO(io.StringIO):
def __init__(self):
super(self, io.StringIO).__init__()
def __init__(self, init_string):
super(ResultStringIO, self).__init__(init_string)
def write(self, data):
super(self, io.StringIO).write(unicode(data))
super(ResultStringIO, self).write(unicode(data))
class GPGTestCase(unittest.TestCase):
@ -216,7 +214,7 @@ class GPGTestCase(unittest.TestCase):
cmd = self.gpg._make_args(None, False)
expected = ['/usr/bin/gpg',
'--status-fd 2 --no-tty',
'--homedir "%s"' % os.path.join(os.getcwd(), 'keys'),
'--homedir "%s"' % HOME_DIR,
'--no-default-keyring --keyring %s' % self.pubring,
'--secret-keyring %s' % self.secring]
self.assertListEqual(cmd, expected)
@ -244,16 +242,19 @@ class GPGTestCase(unittest.TestCase):
"Empty list expected...got instead: %s"
% str(private_keys))
def test_copy_data(self):
"""
XXX implement me
XXX add me to a test suite
Test that _copy_data() is able to duplicate byte streams.
"""
instream = io.BytesIO("This is a string of bytes mapped in memory.")
outstream = str("And this one is just a string.")
def test_copy_data_bytesio(self):
"""Test that _copy_data() is able to duplicate byte streams."""
message = "This is a BytesIO string string in memory."
instream = io.BytesIO(message)
self.assertEqual(unicode(message), instream.getvalue())
outstream = ResultStringIO(u'result:')
copied = outstream
util._copy_data(instream, outstream)
self.assertTrue(outstream.readable())
self.assertTrue(outstream.closed)
self.assertFalse(instream.closed)
self.assertTrue(copied.closed)
#self.assertEqual(instream.getvalue()[6:], outstream.getvalue())
def generate_key_input(self, real_name, email_domain, key_length=None,
key_type=None, subkey_type=None, passphrase=None):
@ -386,9 +387,9 @@ class GPGTestCase(unittest.TestCase):
def test_key_generation_with_empty_value(self):
"""Test that key generation handles empty values."""
params = {'name_comment': ' '}
params = {'name_real': ' '}
batch = self.gpg.gen_key_input(**params)
self.assertTrue('\nName-Comment: Generated by python-gnupg\n' in batch)
self.assertTrue('\nName-Real: Autogenerated Key\n' in batch)
def test_key_generation_override_default_value(self):
"""Test that overriding a default value in gen_key_input() works."""
@ -399,8 +400,9 @@ class GPGTestCase(unittest.TestCase):
def test_list_keys_after_generation(self):
"""Test that after key generation, the generated key is available."""
self.test_list_keys_initial()
self.do_key_generation()
self.test_list_keys_initial_public()
self.test_list_keys_initial_secret()
self.generate_key("Johannes Trithemius", 'iusedcarrierpidgeons@inste.ad')
public_keys = self.gpg.list_keys()
self.assertTrue(is_list_with_len(public_keys, 1),
"1-element list expected")
@ -670,7 +672,8 @@ class GPGTestCase(unittest.TestCase):
suites = { 'parsers': set(['test_parsers_fix_unsafe',
'test_parsers_is_hex_valid',
'test_parsers_is_hex_invalid',]),
'test_parsers_is_hex_invalid',
'test_copy_data_bytesio',]),
'basic': set(['test_gpghome_creation',
'test_gpg_binary',
'test_gpg_binary_not_abs',
@ -768,11 +771,6 @@ if __name__ == "__main__":
suite_names = list()
for name, methodset in suites.items():
suite_names.append(name)
this_file = inspect.getfile(inspect.currentframe()).split('.', 1)[0]
#mod = getattr(this_file, '__dict__', None)
#func = getattr(gnupg.__module__, '__setattr__', None)
#if func is not None:
# func(name, list(methodset))
setattr(GPGTestCase, name, list(methodset))
parser = argparse.ArgumentParser(description="Unittests for python-gnupg")

View File

@ -30,6 +30,11 @@ from datetime import datetime
import logging
import os
import time
import random
import string
import sys
import threading
try:
from io import StringIO
@ -62,6 +67,54 @@ _ugpg = os.path.join(_user, '.gnupg') ## $HOME/.gnupg
_conf = os.path.join(os.path.join(_user, '.config'),
'python-gnupg') ## $HOME/.config/python-gnupg
def _copy_data(instream, outstream):
"""Copy data from one stream to another.
:type instream: :class:`io.BytesIO` or :class:`io.StringIO` or file
:param instream: A byte stream or open file to read from.
:param file outstream: The file descriptor of a tmpfile to write to.
"""
sent = 0
#try:
# #assert (util._is_stream(instream)
# # or isinstance(instream, file)), "instream not stream or file"
# assert isinstance(outstream, file), "outstream is not a file"
#except AssertionError as ae:
# logger.exception(ae)
# return
if hasattr(sys.stdin, 'encoding'):
enc = sys.stdin.encoding
else:
enc = 'ascii'
while True:
data = instream.read(1024)
if len(data) == 0:
break
sent += len(data)
logger.debug("_copy_data(): sending chunk (%d):\n%s" % (sent, data[:256]))
try:
outstream.write(data)
except UnicodeError:
try:
outstream.write(data.encode(enc))
except IOError:
logger.exception('_copy_data(): Error sending data: Broken pipe')
break
except IOError:
# Can get 'broken pipe' errors even when all data was sent
logger.exception('_copy_data(): Error sending data: Broken pipe')
break
try:
outstream.close()
except IOError:
logger.exception('_copy_data(): Got IOError while closing %s' % outstream)
else:
logger.debug("_copy_data(): Closed output, %d bytes sent." % sent)
def _create_gpghome(gpghome):
"""Create the specified GnuPG home directory, if necessary.
@ -181,15 +234,78 @@ def _make_binary_stream(s, encoding):
rv = StringIO(s)
return rv
## xxx unused function?
def _today():
"""Get the current date.
def _make_passphrase(length=None, save=False, file=None):
"""Create a passphrase and write it to a file that only the user can read.
This is not very secure, and should not be relied upon for actual key
passphrases.
:param int length: The length in bytes of the string to generate.
:param file file: The file to save the generated passphrase in. If not
given, defaults to 'passphrase-<the real user id>-<seconds since
epoch>' in the top-level directory.
"""
if not length:
length = 40
passphrase = _make_random_string(length)
if save:
ruid, euid, suid = os.getresuid()
gid = os.getgid()
now = time.mktime(time.gmtime())
if not file:
filename = str('passphrase-%s-%s' % uid, now)
file = os.path.join(_repo, filename)
with open(file, 'a') as fh:
fh.write(passphrase)
fh.flush()
fh.close()
os.chmod(file, 0600)
os.chown(file, ruid, gid)
logger.warn("Generated passphrase saved to %s" % file)
return passphrase
def _make_random_string(length):
"""Returns a random lowercase, uppercase, alphanumerical string.
:param int length: The length in bytes of the string to generate.
"""
chars = string.ascii_lowercase + string.ascii_uppercase + string.digits
return ''.join(random.choice(chars) for x in range(length))
def _next_year():
"""Get the date of today plus one year.
:rtype: str
:returns: The date, in the format '%Y-%m-%d'.
:returns: The date of this day next year, in the format '%Y-%m-%d'.
"""
now_string = datetime.now().__str__()
return now_string.split(' ', 1)[0]
now = datetime.now().__str__()
date = now.split(' ', 1)[0]
year, month, day = date.split('-', 2)
next_year = str(int(year)+1)
return '-'.join((next_year, month, day))
def _threaded_copy_data(instream, outstream):
"""Copy data from one stream to another in a separate thread.
Wraps ``_copy_data()`` in a :class:`threading.Thread`.
:type instream: :class:`io.BytesIO` or :class:`io.StringIO`
:param instream: A byte stream to read from.
:param file outstream: The file descriptor of a tmpfile to write to.
"""
copy_thread = threading.Thread(target=_copy_data,
args=(instream, outstream))
copy_thread.setDaemon(True)
logger.debug('_threaded_copy_data(): %r, %r, %r', copy_thread,
instream, outstream)
copy_thread.start()
return copy_thread
def _which(executable, flags=os.X_OK):
"""Borrowed from Twisted's :mod:twisted.python.proutils .
@ -228,3 +344,9 @@ def _which(executable, flags=os.X_OK):
if os.access(pext, flags):
result.append(pext)
return result
def _write_passphrase(stream, passphrase, encoding):
passphrase = '%s\n' % passphrase
passphrase = passphrase.encode(encoding)
stream.write(passphrase)
logger.debug("_write_passphrase(): Wrote passphrase.")