Skip to content

Commit b23c0de

Browse files
committed
Merge remote-tracking branch 'origin/pr/19'
* origin/pr/19: tests: update Thunderbird/Evolution interactions tests: generate signing subkey tests: log stdout/stderr on key generation failure Do not output keyring import messages to the client stderr Add type annotations to StdoutWriterProtocol ci: drop R4.1 add R4.3 Convert FlowControlMixin to StdoutWriterProtocol Factor in FlowControlMixin tests: fix creating config dir tests: assertEquals -> assertEqual
2 parents 2129a12 + b2fb67d commit b23c0de

6 files changed

Lines changed: 301 additions & 103 deletions

File tree

.gitlab-ci.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
include:
2-
- project: 'QubesOS/qubes-continuous-integration'
3-
file: '/r4.1/gitlab-base.yml'
4-
- project: 'QubesOS/qubes-continuous-integration'
5-
file: '/r4.1/gitlab-dom0.yml'
6-
- project: 'QubesOS/qubes-continuous-integration'
7-
file: '/r4.1/gitlab-vm.yml'
82
- project: 'QubesOS/qubes-continuous-integration'
93
file: '/r4.2/gitlab-base.yml'
104
- project: 'QubesOS/qubes-continuous-integration'
115
file: '/r4.2/gitlab-host.yml'
126
- project: 'QubesOS/qubes-continuous-integration'
137
file: '/r4.2/gitlab-vm.yml'
8+
- project: 'QubesOS/qubes-continuous-integration'
9+
file: '/r4.3/gitlab-base.yml'
10+
- project: 'QubesOS/qubes-continuous-integration'
11+
file: '/r4.3/gitlab-host.yml'
12+
- project: 'QubesOS/qubes-continuous-integration'
13+
file: '/r4.3/gitlab-vm.yml'
1414

1515
checks:tests:
1616
stage: checks

splitgpg2/__init__.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@
4646
from typing import Optional, Dict, Callable, Awaitable, Tuple, Pattern, List, \
4747
Union, Any, TypeVar, Set, TYPE_CHECKING, Coroutine, Sequence, cast
4848

49+
from .stdiostream import StdoutWriterProtocol
50+
4951
if TYPE_CHECKING:
5052
from typing_extensions import Protocol
5153
from typing import TypeAlias
@@ -331,15 +333,21 @@ def setup_subkey_keyring(self) -> None:
331333
self.gnupghome, self.source_keyring_dir)
332334
with subprocess.Popen(export_cmd,
333335
stdout=subprocess.PIPE,
334-
stdin=subprocess.DEVNULL) as exporter, (
336+
stdin=subprocess.DEVNULL,
337+
stderr=subprocess.PIPE) as exporter, (
335338
subprocess.Popen(import_cmd,
336-
stdin=exporter.stdout)) as importer:
339+
stdin=exporter.stdout,
340+
stdout=subprocess.PIPE,
341+
stderr=subprocess.PIPE)) as importer:
337342
pass
338343
if exporter.returncode or importer.returncode:
339344
self.log.warning('Unable to export keys. If your key has a '
340345
'passphrase, you might want to save it to a '
341346
'file and use passphrase-file and '
342-
'pinentry-mode loopback in gpg.conf')
347+
'pinentry-mode loopback in gpg.conf.')
348+
self.log.warning("Exporter output: %s", exporter.stderr)
349+
self.log.warning("Importer output: %s %s",
350+
importer.stdout, importer.stderr)
343351
self.log.info('Subkey-only keyring %r created',
344352
self.gnupghome)
345353

@@ -1453,7 +1461,7 @@ def open_stdinout_connection(*,
14531461

14541462
write_transport, write_protocol = loop.run_until_complete(
14551463
loop.connect_write_pipe(
1456-
lambda: asyncio.streams.FlowControlMixin(loop),
1464+
lambda: StdoutWriterProtocol(loop),
14571465
sys.stdout.buffer))
14581466
writer = asyncio.StreamWriter(write_transport, write_protocol, None, loop)
14591467

splitgpg2/stdiostream.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#
2+
# based on asyncio library:
3+
# Copyright (C) 2001 Python Software Foundation
4+
#
5+
# Copyright (C) 2024 Marek Marczykowski-Górecki
6+
# <marmarek@invisiblethingslab.com>
7+
#
8+
9+
import collections
10+
from asyncio import protocols, events, Future
11+
from typing import Optional, Any
12+
13+
class StdoutWriterProtocol(protocols.Protocol):
14+
"""Reusable flow control logic for StreamWriter.drain().
15+
This implements the protocol methods pause_writing(),
16+
resume_writing() and connection_lost(). If the subclass overrides
17+
these it must call the super methods.
18+
StreamWriter.drain() must wait for _drain_helper() coroutine.
19+
"""
20+
21+
def __init__(self, loop: Optional[events.AbstractEventLoop] = None) -> None:
22+
if loop is None:
23+
self._loop = events.get_event_loop()
24+
else:
25+
self._loop = loop
26+
self._paused = False
27+
self._drain_waiters: collections.deque[Future[None]] = \
28+
collections.deque()
29+
self._connection_lost = False
30+
self._closed = self._loop.create_future()
31+
32+
def pause_writing(self) -> None:
33+
assert not self._paused
34+
self._paused = True
35+
36+
def resume_writing(self) -> None:
37+
assert self._paused
38+
self._paused = False
39+
40+
for waiter in self._drain_waiters:
41+
if not waiter.done():
42+
waiter.set_result(None)
43+
44+
def connection_lost(self, exc: Optional[BaseException]) -> None:
45+
self._connection_lost = True
46+
# Wake up the writer(s) if currently paused.
47+
if not self._paused:
48+
return
49+
50+
for waiter in self._drain_waiters:
51+
if not waiter.done():
52+
if exc is None:
53+
waiter.set_result(None)
54+
else:
55+
waiter.set_exception(exc)
56+
if not self._closed.done():
57+
if exc is None:
58+
self._closed.set_result(None)
59+
else:
60+
self._closed.set_exception(exc)
61+
62+
async def _drain_helper(self) -> None:
63+
if self._connection_lost:
64+
raise ConnectionResetError('Connection lost')
65+
if not self._paused:
66+
return
67+
waiter = self._loop.create_future()
68+
self._drain_waiters.append(waiter)
69+
try:
70+
await waiter
71+
finally:
72+
self._drain_waiters.remove(waiter)
73+
74+
# pylint: disable=unused-argument
75+
def _get_close_waiter(self, stream: Any) -> Future[None]:
76+
return self._closed

0 commit comments

Comments
 (0)