Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pcapkit/foundation/engines/scapy.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ def read_frame(self) -> 'ScapyPacket':
ofile(info, name=frnum)
else:
ext._ofile(info, name=frnum)
ofile = ext._ofile
ext._offmt = ofile.kind

# record fragments
if ext._flag_r:
Expand Down
8 changes: 6 additions & 2 deletions pcapkit/foundation/extraction.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ def output(self) -> 'str':

"""
if self._flag_q:
raise UnsupportedCall("'Extractor(nofile=True)' object has no attribute 'format'")
raise UnsupportedCall("'Extractor(nofile=True)' object has no attribute 'output'")
return cast('str', self._ofnm)

@property
Expand Down Expand Up @@ -809,7 +809,11 @@ def make_name(cls, fin: 'str | IO[bytes]' = 'in.pcap', fout: 'str' = 'out',
ofnm = None
ext = None
else:
registered = cls.__output__[fmt][1]
# NOTE: ``__output__`` is a defaultdict, so look ``fmt`` up with
# :meth:`dict.get` -- subscripting would insert an unknown format
# into the registry before the error below is raised.
entry = cls.__output__.get(fmt)
registered = entry[1] if entry is not None else None
if registered is None:
raise FormatError(f'unknown output format: {fmt}')

Expand Down
71 changes: 71 additions & 0 deletions tests/foundation/test_extraction_output_offmt_unit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# -*- coding: utf-8 -*-
"""Regression tests for three :class:`~pcapkit.foundation.extraction.Extractor` defects -- #1095.

* :attr:`Extractor.output` on a ``nofile=True`` extractor named the wrong
attribute, ``format``, in its :exc:`~pcapkit.utilities.exceptions.UnsupportedCall`.
* :meth:`Extractor.make_name` subscripted the ``__output__`` defaultdict, so an
unknown format was inserted into the registry before
:exc:`~pcapkit.utilities.exceptions.FormatError` was raised.
* The `Scapy`_ engine never set ``Extractor._offmt``, so :attr:`Extractor.format`
raised :exc:`AttributeError` after a file-producing extraction.

.. _Scapy: https://scapy.net

"""
from __future__ import annotations

import importlib.util
import os
import tempfile
import unittest
import warnings

from pcapkit.foundation.extraction import Extractor
from pcapkit.interface import extract
from pcapkit.utilities.exceptions import FormatError, UnsupportedCall
from tests._support import sample_path

HAS_SCAPY = importlib.util.find_spec('scapy') is not None


class OutputMessageTests(unittest.TestCase):
""":attr:`Extractor.output` names itself when output is disabled."""

def test_nofile_output_names_output(self) -> None:
"""The :exc:`UnsupportedCall` message names ``output``, not ``format``."""
with warnings.catch_warnings():
warnings.simplefilter('ignore')
extractor = extract(fin=sample_path('in.pcap'), nofile=True)
with self.assertRaises(UnsupportedCall) as ctx:
extractor.output # pylint: disable=pointless-statement
self.assertEqual(str(ctx.exception),
"'Extractor(nofile=True)' object has no attribute 'output'")


class MakeNameRegistryTests(unittest.TestCase):
"""An unknown format leaves ``Extractor.__output__`` untouched."""

def test_unknown_format_is_not_inserted(self) -> None:
"""The registry keys are unchanged after :exc:`FormatError`."""
before = set(Extractor.__output__)
self.assertNotIn('bogus-1095', before)
with self.assertRaises(FormatError):
Extractor.make_name(sample_path('in.pcap'), None, 'bogus-1095', nofile=False)
self.assertEqual(set(Extractor.__output__), before)


@unittest.skipUnless(HAS_SCAPY, 'scapy not installed')
class ScapyFormatTests(unittest.TestCase):
"""The `Scapy`_ engine records the output format like every other engine."""

def test_scapy_engine_sets_format(self) -> None:
""":attr:`Extractor.format` reads back the requested format."""
with tempfile.TemporaryDirectory() as tmp, warnings.catch_warnings():
warnings.simplefilter('ignore')
extractor = extract(fin=sample_path('in.pcap'), fout=os.path.join(tmp, 'out'),
engine='scapy', format='json', nofile=False)
self.assertEqual(extractor.format, 'json')


if __name__ == '__main__':
unittest.main()
Loading