diff --git a/nh2/mock.py b/nh2/mock.py index 5532188..53ba30e 100644 --- a/nh2/mock.py +++ b/nh2/mock.py @@ -75,30 +75,66 @@ async def flush(self): await self.s.send(data) -def _format(obj, indent=0): - return '\n'.join(_do_format(obj, indent)).replace(' \n', '\n') +def _format(obj): + return ''.join(_do_format(obj, 0)) -def _do_format(obj, indent): +def _simple(obj): + return obj is None or isinstance(obj, (bytes, int, str)) + + +def _do_format(obj, indent, name=''): # pylint: disable=too-many-branches prefix = ' ' * indent - if isinstance(obj, h2.events.RemoteSettingsChanged): - yield '[RemoteSettingsChanged]' - yield prefix + ' '.join(f'{setting.setting.name.lower()}={setting.new_value}' - for setting in obj.changed_settings.values()) - elif isinstance(obj, dict): - yield '' - for k, v in sorted(obj.items()): - k = f'{k}' + + if isinstance(obj, dict): + multiline = False + items = [] + for k, v in obj.items(): if not k.startswith('_'): - yield f'{prefix}{k}: {_format(v, indent + 1)}' + items.append((k, v)) + if not multiline and not _simple(v): + multiline = True + if not multiline: + yield ' [' + if name: + yield f'{name} ' + yield ' '.join(f'{k}={repr(v)}' for k, v in items) + ']\n' + else: + if name: + yield f' [{name}]' + yield '\n' + for k, v in items: + yield f'{prefix}{k}:' + yield from _do_format(v, indent + 1) elif isinstance(obj, list): - yield '' + multiline = False for v in obj: - yield f'{prefix}- {_format(v, indent + 1)}' - elif obj is None or isinstance(obj, (bytes, int, str, tuple)): - yield f'{repr(obj)}' + if not _simple(v): + multiline = True + break + if not multiline: + yield ' [' + if name: + yield f'{name} ' + yield ' '.join(repr(v) for v in obj) + ']\n' + else: + if name: + yield f' [{name}]' + yield '\n' + for v in obj: + yield f'{prefix}-' + yield from _do_format(v, indent + 1) + elif _simple(obj) or isinstance(obj, tuple): + if name: + name = f'[{name}] ' + yield f' {name}{repr(obj)}\n' else: - yield f'[{obj.__class__.__qualname__}] {_format(obj.__dict__, indent)}' + name = obj.__class__.__qualname__ + if isinstance(obj, h2.events.RemoteSettingsChanged): + obj = {chg.setting.name.lower(): chg.new_value for chg in obj.changed_settings.values()} + else: + obj = obj.__dict__ + yield from _do_format(obj, indent, name=name) class _DedentingString(str): diff --git a/nh2/test_mock.py b/nh2/test_mock.py index e2a570c..e9c08ee 100644 --- a/nh2/test_mock.py +++ b/nh2/test_mock.py @@ -15,30 +15,27 @@ async def test_simple(): mock_server = await nh2.mock.expect_connect('example.com', 443) conn = await nh2.connection.Connection('example.com', 443) assert await mock_server.read() == """ - - [RemoteSettingsChanged] - header_table_size=4096 enable_push=1 initial_window_size=65535 max_frame_size=16384 enable_connect_protocol=0 max_concurrent_streams=100 max_header_list_size=65536 + - [RemoteSettingsChanged header_table_size=4096 enable_push=1 initial_window_size=65535 max_frame_size=16384 enable_connect_protocol=0 max_concurrent_streams=100 max_header_list_size=65536] """ live_request = await conn.request('POST', '/dummy', json={'a': 1}) assert await mock_server.read() == """ - [RequestReceived] + stream_id: 1 headers: - (':method', 'POST') - (':path', '/dummy') - (':authority', 'example.com') - (':scheme', 'https') - ('content-type', 'application/json; charset=utf-8') - priority_updated: None stream_ended: None - stream_id: 1 + priority_updated: None - [DataReceived] + stream_id: 1 data: b'{"a":1}' flow_controlled_length: 7 - stream_ended: [StreamEnded] - stream_id: 1 - stream_id: 1 - - [StreamEnded] - stream_id: 1 + stream_ended: [StreamEnded stream_id=1] + - [StreamEnded stream_id=1] """ mock_server.c.send_headers(1, [(':status', '200')]) @@ -49,15 +46,12 @@ async def test_simple(): assert response.body == 'dummy response' assert await mock_server.read() == """ - [SettingsAcknowledged] - changed_settings: + changed_settings: [] """ await conn.close() assert await mock_server.read() == """ - - [ConnectionTerminated] - additional_data: None - error_code: - last_stream_id: 0 + - [ConnectionTerminated error_code= last_stream_id=0 additional_data=None] """ assert await mock_server.read() == """ SOCKET CLOSED @@ -79,23 +73,20 @@ async def opaque_workflow(): future = tg.start_soon(opaque_workflow) assert await mock_server.read() == """ - - [RemoteSettingsChanged] - header_table_size=4096 enable_push=1 initial_window_size=65535 max_frame_size=16384 enable_connect_protocol=0 max_concurrent_streams=100 max_header_list_size=65536 + - [RemoteSettingsChanged header_table_size=4096 enable_push=1 initial_window_size=65535 max_frame_size=16384 enable_connect_protocol=0 max_concurrent_streams=100 max_header_list_size=65536] """ assert await mock_server.read() == """ - [RequestReceived] + stream_id: 1 headers: - (':method', 'GET') - (':path', '/dummy') - (':authority', 'example.com') - (':scheme', 'https') + stream_ended: [StreamEnded stream_id=1] priority_updated: None - stream_ended: [StreamEnded] - stream_id: 1 - stream_id: 1 - - [StreamEnded] - stream_id: 1 + - [StreamEnded stream_id=1] """ mock_server.c.send_headers(1, [(':status', '200')])