Retrieve data from db result before returning from Context Manager
SQLAlchemy's connection is a Context Manager and if we return a result from code wrapped in a Context Manager, its cursor might already be closed.
This commit is contained in:
parent
8d2bf403a7
commit
bfd3541b18
5 changed files with 30 additions and 15 deletions
|
@ -41,6 +41,9 @@ def sub_queue(args):
|
|||
elif args.list:
|
||||
for k in queue.fetch_keys():
|
||||
print(f'- {k.id}: {k.email}')
|
||||
elif args.to_delete:
|
||||
for k in queue.fetch_keys_to_delete():
|
||||
print(f'- {k.id}: {k.email}')
|
||||
else:
|
||||
cnt = queue.count_keys()
|
||||
if cnt is None:
|
||||
|
@ -125,6 +128,8 @@ def main():
|
|||
help='delete specified email from the queue')
|
||||
cmd_queue.add_argument('-l', '--list', action='store_true',
|
||||
help='list keys in the queue')
|
||||
cmd_queue.add_argument('-d', '--to-delete', action='store_true',
|
||||
help='list keys to be deleted')
|
||||
cmd_queue.set_defaults(operation=sub_queue)
|
||||
|
||||
cmd_identities = sub_commands.add_parser('identities',
|
||||
|
|
|
@ -12,15 +12,22 @@ This definition includes:
|
|||
import sqlalchemy
|
||||
|
||||
# Values for lacre_keys.status column:
|
||||
# - ST_DEFAULT: initial state;
|
||||
# - ST_IMPORTED: key has been successfully processed by cron job;
|
||||
# - ST_TO_BE_DELETED: key can be deleted.
|
||||
ST_DEFAULT, ST_IMPORTED, ST_TO_BE_DELETED = range(3)
|
||||
|
||||
# lacre_keys.confirmed is set to an empty string when a key is confirmed by the user.
|
||||
CO_CONFIRMED = ''
|
||||
|
||||
_meta = sqlalchemy.MetaData()
|
||||
|
||||
LACRE_KEYS = sqlalchemy.Table('lacre_keys', _meta,
|
||||
sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True),
|
||||
sqlalchemy.Column('email', sqlalchemy.String(256)),
|
||||
sqlalchemy.Column('email', sqlalchemy.String(256), index=True),
|
||||
# ASCII-armored key
|
||||
sqlalchemy.Column('publickey', sqlalchemy.Text),
|
||||
# Empty string means this key has been confirmed.
|
||||
sqlalchemy.Column('confirm', sqlalchemy.String(32)),
|
||||
# Status: see ST_* constants at the top of the file.
|
||||
sqlalchemy.Column('status', sqlalchemy.Integer),
|
||||
|
|
|
@ -139,33 +139,37 @@ class KeyConfirmationQueue:
|
|||
def fetch_keys(self, /, max_keys=None):
|
||||
"""Runs a query to retrieve at most `keys_read_max` keys and returns db result."""
|
||||
max_keys = max_keys or self.keys_read_max
|
||||
LOG.debug('Row limit: %d', max_keys)
|
||||
|
||||
selq = select(self._keys.c.publickey, self._keys.c.id, self._keys.c.email) \
|
||||
.where(and_(self._keys.c.status == db.ST_DEFAULT, self._keys.c.confirm == "")) \
|
||||
.where(and_(self._keys.c.status == db.ST_DEFAULT, self._keys.c.confirm == db.CO_CONFIRMED)) \
|
||||
.limit(max_keys)
|
||||
|
||||
LOG.debug('Retrieving keys to be processed: %s -- %s', selq, selq.compile().params)
|
||||
with self._engine.connect() as conn:
|
||||
return conn.execute(selq)
|
||||
return [e for e in conn.execute(selq)]
|
||||
|
||||
def count_keys(self):
|
||||
selq = select(func.count(self._keys.c.id))
|
||||
selq = select(func.count(self._keys.c.id)) \
|
||||
.where(and_(self._keys.c.status == db.ST_DEFAULT, self._keys.c.confirm == db.CO_CONFIRMED))
|
||||
|
||||
LOG.debug('Counting all keys: %s -- %s', selq, selq.compile().params)
|
||||
try:
|
||||
with self._engine.connect() as conn:
|
||||
c = [cnt for cnt in conn.execute(selq)]
|
||||
|
||||
# Result is an iterable of tuples:
|
||||
return c[0][0]
|
||||
res = conn.execute(selq)
|
||||
# This is a 1-element tuple.
|
||||
return res.one_or_none()[0]
|
||||
except OperationalError:
|
||||
LOG.exception('Cannot count keys')
|
||||
return None
|
||||
|
||||
def fetch_keys_to_delete(self):
|
||||
seldel = select(self._keys.c.email, self._keys.c.id).where(self._keys.c.status == db.ST_TO_BE_DELETED).limit(self.keys_read_max)
|
||||
seldel = select(self._keys.c.email, self._keys.c.id) \
|
||||
.where(self._keys.c.status == db.ST_TO_BE_DELETED) \
|
||||
.limit(self.keys_read_max)
|
||||
|
||||
with self._engine.connect() as conn:
|
||||
return conn.execute(seldel)
|
||||
return [e for e in conn.execute(seldel)]
|
||||
|
||||
def delete_keys(self, row_id, /, email=None):
|
||||
"""Remove key from the database."""
|
||||
|
@ -178,7 +182,7 @@ class KeyConfirmationQueue:
|
|||
|
||||
with self._engine.connect() as conn:
|
||||
LOG.debug('Deleting public keys associated with confirmed email: %s', delq)
|
||||
return conn.execute(delq)
|
||||
conn.execute(delq)
|
||||
|
||||
def delete_key_by_email(self, email):
|
||||
"""Remove keys linked to the given email from the database."""
|
||||
|
|
|
@ -90,8 +90,8 @@ AQgHiHgEGBYIACAWIQQZz0tH7MnEevqE1L2W85/aDjG7ZwUCYdTF4AIbDAAKCRCW\n\
|
|||
OjjB6xRD0Q2FN+alsNGCtdutAs18AZ5l33RMzws=\n\
|
||||
=wWoq\n\
|
||||
-----END PGP PUBLIC KEY BLOCK-----\
|
||||
", "status": 0, "confirm": "", "time": None},
|
||||
{"id": 3, "email": "cecil@lacre.io", "publickey": "RUBBISH", "status": 0, "confirm": "", "time": None}
|
||||
", "status": 1, "confirm": "", "time": None},
|
||||
{"id": 3, "email": "cecil@lacre.io", "publickey": "RUBBISH", "status": 2, "confirm": "", "time": None}
|
||||
])
|
||||
|
||||
conn.execute(identities.insert(), [
|
||||
|
|
|
@ -94,8 +94,7 @@ try:
|
|||
notify("PGP key deleted", "keyDeleted.md", email)
|
||||
|
||||
LOG.info('Cleaning up after a round of key confirmation')
|
||||
stat2_result_set = key_queue.fetch_keys_to_delete()
|
||||
for email, row_id in stat2_result_set:
|
||||
for email, row_id in key_queue.fetch_keys_to_delete():
|
||||
LOG.debug('Removing key from keyring: %s', email)
|
||||
GnuPG.delete_key(key_dir, email)
|
||||
|
||||
|
|
Loading…
Reference in a new issue