diff --git a/src/plugins/indicator_libmessaging.py b/src/plugins/indicator_libmessaging.py index 36178663..ab2e833e 100644 --- a/src/plugins/indicator_libmessaging.py +++ b/src/plugins/indicator_libmessaging.py @@ -1,4 +1,8 @@ # -*- coding: utf-8 -*- +""" +src/plugins/indicator_libmessaging.py +===================================== +""" import gi gi.require_version('MessagingMenu', '1.0') # noqa:E402 @@ -9,6 +13,7 @@ from pybitmessage.tr import _translate class IndicatorLibmessaging(object): + """Plugin for libmessage indicator""" def __init__(self, form): try: self.app = MessagingMenu.App(desktop_id='pybitmessage.desktop') @@ -32,15 +37,18 @@ class IndicatorLibmessaging(object): if self.app: self.app.unregister() - def activate(self, app, source): + def activate(self, app, source): # pylint: disable=unused-argument + """Activate the libmessaging indicator plugin""" self.form.appIndicatorInbox( self.new_message_item if source == 'messages' else self.new_broadcast_item ) - # show the number of unread messages and subscriptions - # on the messaging menu def show_unread(self, draw_attention=False): + """ + show the number of unread messages and subscriptions + on the messaging menu + """ for source, count in zip( ('messages', 'subscriptions'), self.form.getUnread() diff --git a/src/plugins/menu_qrcode.py b/src/plugins/menu_qrcode.py index c5a21ac3..1c2ebe44 100644 --- a/src/plugins/menu_qrcode.py +++ b/src/plugins/menu_qrcode.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- """ +src/plugins/menu_qrcode.py +========================== + A menu plugin showing QR-Code for bitmessage address in modal dialog. """ @@ -12,9 +15,10 @@ from pybitmessage.tr import _translate # http://stackoverflow.com/questions/20452486 -class Image(qrcode.image.base.BaseImage): +class Image(qrcode.image.base.BaseImage): # pylint: disable=abstract-method """Image output class for qrcode using QPainter""" - def __init__(self, border, width, box_size): + + def __init__(self, border, width, box_size): # pylint: disable=super-init-not-called self.border = border self.width = width self.box_size = box_size diff --git a/src/plugins/notification_notify2.py b/src/plugins/notification_notify2.py index 3fd935c4..b4cd045d 100644 --- a/src/plugins/notification_notify2.py +++ b/src/plugins/notification_notify2.py @@ -1,4 +1,8 @@ # -*- coding: utf-8 -*- +""" +src/plugins/notification_notify2.py +=================================== +""" import gi gi.require_version('Notify', '0.7') @@ -6,10 +10,13 @@ from gi.repository import Notify Notify.init('pybitmessage') + def connect_plugin(title, subtitle, category, label, icon): + """Plugin for notify2""" if not icon: icon = 'mail-message-new' if category == 2 else 'pybitmessage' connect_plugin.notification.update(title, subtitle, icon) connect_plugin.notification.show() + connect_plugin.notification = Notify.Notification.new("Init", "Init") diff --git a/src/plugins/plugin.py b/src/plugins/plugin.py index 6601adaf..e671a73f 100644 --- a/src/plugins/plugin.py +++ b/src/plugins/plugin.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- - +""" +src/plugins/plugin.py +=================================== +""" import pkg_resources diff --git a/src/plugins/proxyconfig_stem.py b/src/plugins/proxyconfig_stem.py index e8b5417e..5bb9a726 100644 --- a/src/plugins/proxyconfig_stem.py +++ b/src/plugins/proxyconfig_stem.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- - +""" +src/plugins/proxyconfig_stem.py +=================================== +""" import os import logging import random # noseq @@ -29,14 +32,14 @@ class DebugLogger(object): # Plugin's debug or unexpected log line from tor self._logger.debug(line) else: - self._logger.log(self._levels.get(level, 10), '(tor)' + line) + self._logger.log(self._levels.get(level, 10), '(tor) %s', line) def connect_plugin(config): # pylint: disable=too-many-branches """Run stem proxy configurator""" logwrite = DebugLogger() if config.safeGet('bitmessagesettings', 'sockshostname') not in ( - 'localhost', '127.0.0.1', '' + 'localhost', '127.0.0.1', '' ): # remote proxy is choosen for outbound connections, # nothing to do here, but need to set socksproxytype to SOCKS5! diff --git a/src/plugins/sound_canberra.py b/src/plugins/sound_canberra.py index 094901ed..dbb4baed 100644 --- a/src/plugins/sound_canberra.py +++ b/src/plugins/sound_canberra.py @@ -1,4 +1,8 @@ # -*- coding: utf-8 -*- +""" +src/plugins/proxyconfig_stem.py +=================================== +""" from pybitmessage.bitmessageqt import sound @@ -14,7 +18,8 @@ _theme = { } -def connect_plugin(category, label=None): +def connect_plugin(category, label=None): # pylint: disable=unused-argument + """This function implements the entry point.""" try: _canberra.play(0, pycanberra.CA_PROP_EVENT_ID, _theme[category], None) except (KeyError, pycanberra.CanberraException): diff --git a/src/plugins/sound_gstreamer.py b/src/plugins/sound_gstreamer.py index 062da3f9..32a0aa65 100644 --- a/src/plugins/sound_gstreamer.py +++ b/src/plugins/sound_gstreamer.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- - +""" +src/plugins/sound_gstreamer.py +=================================== +""" import gi gi.require_version('Gst', '1.0') from gi.repository import Gst # noqa: E402 @@ -9,6 +12,7 @@ _player = Gst.ElementFactory.make("playbin", "player") def connect_plugin(sound_file): + """Entry point for sound file""" _player.set_state(Gst.State.NULL) _player.set_property("uri", "file://" + sound_file) _player.set_state(Gst.State.PLAYING) diff --git a/src/plugins/sound_playfile.py b/src/plugins/sound_playfile.py index c8216d07..6396c319 100644 --- a/src/plugins/sound_playfile.py +++ b/src/plugins/sound_playfile.py @@ -1,10 +1,14 @@ # -*- coding: utf-8 -*- - +""" +src/plugins/sound_playfile.py +=================================== +""" try: import winsound def connect_plugin(sound_file): + """Plugin's entry point""" winsound.PlaySound(sound_file, winsound.SND_FILENAME) except ImportError: import os @@ -18,7 +22,8 @@ except ImportError: args, stdout=FNULL, stderr=subprocess.STDOUT, close_fds=True) def connect_plugin(sound_file): - global play_cmd + """This function implements the entry point.""" + global play_cmd # pylint: disable=global-statement ext = os.path.splitext(sound_file)[-1] try: diff --git a/src/pyelliptic/__init__.py b/src/pyelliptic/__init__.py index 1d6a928f..7aa666e0 100644 --- a/src/pyelliptic/__init__.py +++ b/src/pyelliptic/__init__.py @@ -1,3 +1,7 @@ +""" +src/pyelliptic/__init__.py +===================================== +""" # Copyright (C) 2010 # Author: Yann GUIBET # Contact: diff --git a/src/pyelliptic/cipher.py b/src/pyelliptic/cipher.py index bc1af6b0..d02b743a 100644 --- a/src/pyelliptic/cipher.py +++ b/src/pyelliptic/cipher.py @@ -1,5 +1,9 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +""" +src/pyelliptic/cipher.py +======================== +""" # Copyright (C) 2011 Yann GUIBET # See LICENSE for details. @@ -7,7 +11,8 @@ from openssl import OpenSSL -class Cipher: +# pylint: disable=redefined-builtin +class Cipher(object): """ Symmetric encryption @@ -44,30 +49,34 @@ class Cipher: @staticmethod def get_blocksize(ciphername): + """This Method returns cipher blocksize""" cipher = OpenSSL.get_cipher(ciphername) return cipher.get_blocksize() @staticmethod def gen_IV(ciphername): + """Generate random initialization vector""" cipher = OpenSSL.get_cipher(ciphername) return OpenSSL.rand(cipher.get_blocksize()) def update(self, input): + """Update result with more data""" i = OpenSSL.c_int(0) buffer = OpenSSL.malloc(b"", len(input) + self.cipher.get_blocksize()) inp = OpenSSL.malloc(input, len(input)) if OpenSSL.EVP_CipherUpdate(self.ctx, OpenSSL.byref(buffer), OpenSSL.byref(i), inp, len(input)) == 0: raise Exception("[OpenSSL] EVP_CipherUpdate FAIL ...") - return buffer.raw[0:i.value] + return buffer.raw[0:i.value] # pylint: disable=invalid-slice-index def final(self): + """Returning the final value""" i = OpenSSL.c_int(0) buffer = OpenSSL.malloc(b"", self.cipher.get_blocksize()) if (OpenSSL.EVP_CipherFinal_ex(self.ctx, OpenSSL.byref(buffer), OpenSSL.byref(i))) == 0: raise Exception("[OpenSSL] EVP_CipherFinal_ex FAIL ...") - return buffer.raw[0:i.value] + return buffer.raw[0:i.value] # pylint: disable=invalid-slice-index def ciphering(self, input): """ @@ -77,6 +86,7 @@ class Cipher: return buff + self.final() def __del__(self): + # pylint: disable=protected-access if OpenSSL._hexversion > 0x10100000 and not OpenSSL._libreSSL: OpenSSL.EVP_CIPHER_CTX_reset(self.ctx) else: diff --git a/src/pyelliptic/hash.py b/src/pyelliptic/hash.py index f2240500..c21dd6a4 100644 --- a/src/pyelliptic/hash.py +++ b/src/pyelliptic/hash.py @@ -1,6 +1,9 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- - +""" +src/pyelliptic/hash.py +===================== +""" # Copyright (C) 2011 Yann GUIBET # See LICENSE for details. @@ -27,10 +30,10 @@ def _equals_str(a, b): def equals(a, b): + """Compare two strings or bytearrays""" if isinstance(a, str): return _equals_str(a, b) - else: - return _equals_bytes(a, b) + return _equals_bytes(a, b) def hmac_sha256(k, m): @@ -58,6 +61,7 @@ def hmac_sha512(k, m): def pbkdf2(password, salt=None, i=10000, keylen=64): + """Key derivation function using SHA256""" if salt is None: salt = OpenSSL.rand(8) p_password = OpenSSL.malloc(password, len(password)) diff --git a/src/pyelliptic/openssl.py b/src/pyelliptic/openssl.py index 2d5ea04a..17bf52c8 100644 --- a/src/pyelliptic/openssl.py +++ b/src/pyelliptic/openssl.py @@ -11,16 +11,23 @@ OpenSSL = None # !/usr/bin/env python # -*- coding: utf-8 -*- - +""" +src/pyelliptic/openssl.py +===================== +""" # Copyright (C) 2011 Yann GUIBET # See LICENSE for details. # # Software slightly changed by Jonathan Warren +# pylint: disable=protected-access -class CipherName: # pylint: disable=old-style-class - """Method helps to get pointers, name and blocksize""" + +class CipherName: + """Class returns cipher name, pointer and blocksize""" + + # pylint: disable=old-style-class def __init__(self, name, pointer, blocksize): self._name = name self._pointer = pointer @@ -36,11 +43,11 @@ class CipherName: # pylint: disable=old-style-class return self._pointer() def get_name(self): - """Method returns the name""" + """This method returns cipher name""" return self._name def get_blocksize(self): - """Method returns the blocksize""" + """This method returns cipher blocksize""" return self._blocksize @@ -76,9 +83,11 @@ def get_version(library): return (version, hexversion, cflags) -class _OpenSSL: # pylint: disable=too-many-instance-attributes, old-style-class, too-many-statements - """Wrapper for OpenSSL using ctypes""" - +class _OpenSSL: + """ + Wrapper for OpenSSL using ctypes + """ + # pylint: disable=too-many-statements, too-many-instance-attributes, old-style-class def __init__(self, library): """Build the wrapper""" self._lib = ctypes.CDLL(library) @@ -647,8 +656,7 @@ class _OpenSSL: # pylint: disable=too-many-instance-attributes, old-style-cl def loadOpenSSL(): """Method find and load the OpenSSL library""" - # pylint: disable=global-statement, protected-access, too-many-branches, no-member - + # pylint: disable=global-statement, protected-access, too-many-branches global OpenSSL from os import path, environ from ctypes.util import find_library diff --git a/src/storage/filesystem.py b/src/storage/filesystem.py index d64894a9..43ba03fc 100644 --- a/src/storage/filesystem.py +++ b/src/storage/filesystem.py @@ -1,21 +1,26 @@ +""" +src/storage/filesystem.py +========================= +""" from binascii import hexlify, unhexlify from os import listdir, makedirs, path, remove, rmdir import string from threading import RLock import time -import traceback from paths import lookupAppdataFolder from storage import InventoryStorage, InventoryItem -class FilesystemInventory(InventoryStorage): + +class FilesystemInventory(InventoryStorage): # pylint: disable=too-many-ancestors, abstract-method + """Module for using filesystem (directory with files) for inventory storage""" topDir = "inventory" objectDir = "objects" metadataFilename = "metadata" dataFilename = "data" def __init__(self): - super(self.__class__, self).__init__() + super(FilesystemInventory, self).__init__() self.baseDir = path.join(lookupAppdataFolder(), FilesystemInventory.topDir) for createDir in [self.baseDir, path.join(self.baseDir, "objects")]: if path.exists(createDir): @@ -23,72 +28,101 @@ class FilesystemInventory(InventoryStorage): raise IOError("%s exists but it's not a directory" % (createDir)) else: makedirs(createDir) - self.lock = RLock() # Guarantees that two receiveDataThreads don't receive and process the same message concurrently (probably sent by a malicious individual) + # Guarantees that two receiveDataThreads don't receive and process the same message + # concurrently (probably sent by a malicious individual) + self.lock = RLock() self._inventory = {} self._load() - def __contains__(self, hash): + def __contains__(self, hashval): retval = False for streamDict in self._inventory.values(): - if hash in streamDict: + if hashval in streamDict: return True return False - def __getitem__(self, hash): + def __getitem__(self, hashval): for streamDict in self._inventory.values(): try: - retval = streamDict[hash] + retval = streamDict[hashval] except KeyError: continue if retval.payload is None: - retval = InventoryItem(retval.type, retval.stream, self.getData(hash), retval.expires, retval.tag) + retval = InventoryItem(retval.type, retval.stream, self.getData(hashval), retval.expires, retval.tag) return retval - raise KeyError(hash) + raise KeyError(hashval) - def __setitem__(self, hash, value): + def __setitem__(self, hashval, value): with self.lock: value = InventoryItem(*value) try: - makedirs(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hash))) + makedirs(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hashval))) except OSError: pass try: - with open(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hash), FilesystemInventory.metadataFilename), 'w') as f: + with open( + path.join( + self.baseDir, + FilesystemInventory.objectDir, + hexlify(hashval), + FilesystemInventory.metadataFilename, + ), + "w", + ) as f: f.write("%s,%s,%s,%s," % (value.type, value.stream, value.expires, hexlify(value.tag))) - with open(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hash), FilesystemInventory.dataFilename), 'w') as f: + with open( + path.join( + self.baseDir, + FilesystemInventory.objectDir, + hexlify(hashval), + FilesystemInventory.dataFilename, + ), + "w", + ) as f: f.write(value.payload) except IOError: raise KeyError try: - self._inventory[value.stream][hash] = value + self._inventory[value.stream][hashval] = value except KeyError: self._inventory[value.stream] = {} - self._inventory[value.stream][hash] = value + self._inventory[value.stream][hashval] = value - def delHashId(self, hash): - for stream in self._inventory.keys(): + def delHashId(self, hashval): + """Remove object from inventory""" + for stream in self._inventory: try: - del self._inventory[stream][hash] + del self._inventory[stream][hashval] except KeyError: pass with self.lock: try: - remove(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hash), FilesystemInventory.metadataFilename)) + remove( + path.join( + self.baseDir, + FilesystemInventory.objectDir, + hexlify(hashval), + FilesystemInventory.metadataFilename)) except IOError: pass try: - remove(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hash), FilesystemInventory.dataFilename)) + remove( + path.join( + self.baseDir, + FilesystemInventory.objectDir, + hexlify(hashval), + FilesystemInventory.dataFilename)) except IOError: pass try: - rmdir(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hash))) + rmdir(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hashval))) except IOError: pass def __iter__(self): elems = [] for streamDict in self._inventory.values(): - elems.extend (streamDict.keys()) + elems.extend(streamDict.keys()) return elems.__iter__() def __len__(self): @@ -103,44 +137,66 @@ class FilesystemInventory(InventoryStorage): try: objectType, streamNumber, expiresTime, tag = self.getMetadata(hashId) try: - newInventory[streamNumber][hashId] = InventoryItem(objectType, streamNumber, None, expiresTime, tag) + newInventory[streamNumber][hashId] = InventoryItem( + objectType, streamNumber, None, expiresTime, tag) except KeyError: newInventory[streamNumber] = {} - newInventory[streamNumber][hashId] = InventoryItem(objectType, streamNumber, None, expiresTime, tag) + newInventory[streamNumber][hashId] = InventoryItem( + objectType, streamNumber, None, expiresTime, tag) except KeyError: print "error loading %s" % (hexlify(hashId)) - pass self._inventory = newInventory # for i, v in self._inventory.items(): # print "loaded stream: %s, %i items" % (i, len(v)) def stream_list(self): + """Return list of streams""" return self._inventory.keys() def object_list(self): + """Return inventory vectors (hashes) from a directory""" return [unhexlify(x) for x in listdir(path.join(self.baseDir, FilesystemInventory.objectDir))] def getData(self, hashId): + """Get object data""" try: - with open(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hashId), FilesystemInventory.dataFilename), 'r') as f: + with open( + path.join( + self.baseDir, + FilesystemInventory.objectDir, + hexlify(hashId), + FilesystemInventory.dataFilename, + ), + "r", + ) as f: return f.read() except IOError: raise AttributeError def getMetadata(self, hashId): + """Get object metadata""" try: - with open(path.join(self.baseDir, FilesystemInventory.objectDir, hexlify(hashId), FilesystemInventory.metadataFilename), 'r') as f: + with open( + path.join( + self.baseDir, + FilesystemInventory.objectDir, + hexlify(hashId), + FilesystemInventory.metadataFilename, + ), + "r", + ) as f: objectType, streamNumber, expiresTime, tag, undef = string.split(f.read(), ",", 4) return [int(objectType), int(streamNumber), int(expiresTime), unhexlify(tag)] except IOError: raise KeyError def by_type_and_tag(self, objectType, tag): + """Get a list of objects filtered by object type and tag""" retval = [] for stream, streamDict in self._inventory: for hashId, item in streamDict: if item.type == objectType and item.tag == tag: - try: + try: if item.payload is None: item.payload = self.getData(hashId) except IOError: @@ -149,12 +205,14 @@ class FilesystemInventory(InventoryStorage): return retval def hashes_by_stream(self, stream): + """Return inventory vectors (hashes) for a stream""" try: return self._inventory[stream].keys() except KeyError: return [] def unexpired_hashes_by_stream(self, stream): + """Return unexpired hashes in the inventory for a particular stream""" t = int(time.time()) try: return [x for x, value in self._inventory[stream].items() if value.expires > t] @@ -162,9 +220,11 @@ class FilesystemInventory(InventoryStorage): return [] def flush(self): + """Flush the inventory and create a new, empty one""" self._load() def clean(self): + """Clean out old items from the inventory""" minTime = int(time.time()) - (60 * 60 * 30) deletes = [] for stream, streamDict in self._inventory.items(): diff --git a/src/storage/sqlite.py b/src/storage/sqlite.py index 438cbdcb..0c2b4afa 100644 --- a/src/storage/sqlite.py +++ b/src/storage/sqlite.py @@ -1,45 +1,59 @@ -import collections -from threading import current_thread, enumerate as threadingEnumerate, RLock -import Queue +""" +src/storage/sqlite.py +========================= +""" import sqlite3 import time +from threading import RLock -from helper_sql import * +from helper_sql import sqlQuery, SqlBulkExecute, sqlExecute from storage import InventoryStorage, InventoryItem -class SqliteInventory(InventoryStorage): - def __init__(self): - super(self.__class__, self).__init__() - self._inventory = {} #of objects (like msg payloads and pubkey payloads) Does not include protocol headers (the first 24 bytes of each packet). - self._objects = {} # cache for existing objects, used for quick lookups if we have an object. This is used for example whenever we receive an inv message from a peer to check to see what items are new to us. We don't delete things out of it; instead, the singleCleaner thread clears and refills it. - self.lock = RLock() # Guarantees that two receiveDataThreads don't receive and process the same message concurrently (probably sent by a malicious individual) - def __contains__(self, hash): +class SqliteInventory(InventoryStorage): # pylint: disable=too-many-ancestors + """Inventory using SQLite""" + def __init__(self): + super(SqliteInventory, self).__init__() + # of objects (like msg payloads and pubkey payloads) + # Does not include protocol headers (the first 24 bytes of each packet). + self._inventory = {} + # cache for existing objects, used for quick lookups if we have an object. + # This is used for example whenever we receive an inv message from a peer + # to check to see what items are new to us. + # We don't delete things out of it; instead, the singleCleaner thread clears and refills it. + self._objects = {} + # Guarantees that two receiveDataThreads don't receive and process the same message concurrently + # (probably sent by a malicious individual) + self.lock = RLock() + + def __contains__(self, hash_): with self.lock: - if hash in self._objects: + if hash_ in self._objects: return True - rows = sqlQuery('SELECT streamnumber FROM inventory WHERE hash=?', sqlite3.Binary(hash)) + rows = sqlQuery('SELECT streamnumber FROM inventory WHERE hash=?', sqlite3.Binary(hash_)) if not rows: return False - self._objects[hash] = rows[0][0] + self._objects[hash_] = rows[0][0] return True - def __getitem__(self, hash): + def __getitem__(self, hash_): with self.lock: - if hash in self._inventory: - return self._inventory[hash] - rows = sqlQuery('SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE hash=?', sqlite3.Binary(hash)) + if hash_ in self._inventory: + return self._inventory[hash_] + rows = sqlQuery( + 'SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE hash=?', + sqlite3.Binary(hash_)) if not rows: - raise KeyError(hash) + raise KeyError(hash_) return InventoryItem(*rows[0]) - def __setitem__(self, hash, value): + def __setitem__(self, hash_, value): with self.lock: value = InventoryItem(*value) - self._inventory[hash] = value - self._objects[hash] = value.stream + self._inventory[hash_] = value + self._objects[hash_] = value.stream - def __delitem__(self, hash): + def __delitem__(self, hash_): raise NotImplementedError def __iter__(self): @@ -55,18 +69,22 @@ class SqliteInventory(InventoryStorage): def by_type_and_tag(self, objectType, tag): with self.lock: values = [value for value in self._inventory.values() if value.type == objectType and value.tag == tag] - values += (InventoryItem(*value) for value in sqlQuery('SELECT objecttype, streamnumber, payload, expirestime, tag FROM inventory WHERE objecttype=? AND tag=?', objectType, sqlite3.Binary(tag))) + values += (InventoryItem(*value) for value in sqlQuery( + 'SELECT objecttype, streamnumber, payload, expirestime, tag \ + FROM inventory WHERE objecttype=? AND tag=?', objectType, sqlite3.Binary(tag))) return values def unexpired_hashes_by_stream(self, stream): with self.lock: t = int(time.time()) hashes = [x for x, value in self._inventory.items() if value.stream == stream and value.expires > t] - hashes += (str(payload) for payload, in sqlQuery('SELECT hash FROM inventory WHERE streamnumber=? AND expirestime>?', stream, t)) + hashes += (str(payload) for payload, in sqlQuery( + 'SELECT hash FROM inventory WHERE streamnumber=? AND expirestime>?', stream, t)) return hashes def flush(self): - with self.lock: # If you use both the inventoryLock and the sqlLock, always use the inventoryLock OUTSIDE of the sqlLock. + with self.lock: + # If you use both the inventoryLock and the sqlLock, always use the inventoryLock OUTSIDE of the sqlLock. with SqlBulkExecute() as sql: for objectHash, value in self._inventory.items(): sql.execute('INSERT INTO inventory VALUES (?, ?, ?, ?, ?, ?)', sqlite3.Binary(objectHash), *value) @@ -74,8 +92,7 @@ class SqliteInventory(InventoryStorage): def clean(self): with self.lock: - sqlExecute('DELETE FROM inventory WHERE expirestime