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
22 changes: 16 additions & 6 deletions pcapkit/all.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@
# 'vendor',

# pcapkit.corekit
'Info', # Info Class
'EnumRegistry', # Enum Registry
'Info', 'info_final', # Info Class
'ProtoChain', # ProtoChain
'VersionInfo', # Version
'NumberField', 'Int32Field', 'UInt32Field', # numeric protocol fields
Expand All @@ -65,16 +66,19 @@
'IPv4InterfaceField', 'IPv6InterfaceField',
'ListField', 'OptionField', # container protocol fields
'SeekableReader', # Seekable Reader
'MultiDict', 'OrderedMultiDict', # Multi Dict
'ModuleDescriptor', # Module Descriptor

# pcapkit.dumpkit
'PCAPIO', # PCAP Dumper
'NotImplementedIO', # Simulated I/O

# pcapkit.foundation
'Extractor', # Extraction
'TCP_TraceFlow', # Trace Flow
'TraceFlowManager', 'TCP_TraceFlow', # Trace Flow

# pcapkit.foundation.reassembly
'ReassemblyManager', # Reassembly Manager
'IPv4_Reassembly', 'IPv6_Reassembly', # IP Reassembly
'TCP_Reassembly', # TCP Reassembly

Expand All @@ -97,6 +101,9 @@
'register_extractor_engine',
'register_dumper',
'register_extractor_dumper', 'register_traceflow_dumper',
'register_extractor_reassembly', 'register_extractor_traceflow',
'register_reassembly_ipv4_callback', 'register_reassembly_ipv6_callback',
'register_reassembly_tcp_callback', 'register_traceflow_tcp_callback',

# pcapkit.interface
'extract', 'reassemble', 'trace', # Interface Functions
Expand All @@ -108,16 +115,17 @@
# pcapkit.protocols
'LINKTYPE', 'ETHERTYPE', 'TRANSTYPE', 'APPTYPE', # Protocol Numbers
'Header', 'Frame', # PCAP Headers
'PCAPNG', # PCAPNG Format
'NoPayload', # No Payload
'Raw', # Raw Packet
'ARP', 'C_Tag', 'Ethernet', 'InARP', 'L2TP', 'L2TPv2',
'S_Tag', 'VLAN', # Link Layer
'AH', 'IP', 'IPsec', 'IPv4', 'IPv6', 'IPX', # Internet Layer
'HIP', 'HOPOPT', 'IPv6_Frag', 'IPv6_Opts', 'IPv6_Route', 'MH',
'AH', 'ESP', 'IP', 'IPsec', 'IPv4', 'IPv6', 'IPX', # Internet Layer
'HIP', 'HOPOPT', 'IPv6_Ext', 'IPv6_Frag', 'IPv6_Opts', 'IPv6_Route', 'MH',
# IPv6 Extension Header
'TCP', 'UDP', 'SCTP', # Transport Layer
'FTP', 'FTP_DATA', # Application Layer
'HTTP', 'NGAP', 'OSPF', 'RARP', 'DRARP',
'HTTP', 'HTTPv1', 'HTTPv2', 'NGAP', 'OSPF', 'RARP', 'DRARP',
'Schema', 'schema', # Protocol Schema
'Data', 'data', # Protocol Data

Expand All @@ -133,6 +141,8 @@
'scapy_ipv4_reassembly', 'scapy_ipv6_reassembly', 'scapy_tcp_reassembly', 'scapy_tcp_traceflow',
# Scapy engine

# pcapkit.utilities
# pcapkit.utilities -- unlisted since 769a17c78 (2022-05-29): its names stay
# reachable as attributes, but ``import *`` leaves out generic helpers such
# as ``warn``, ``reset``, ``configure`` and ``detect``
#'beholder_ng', 'seekset_ng', # Decorators
]
83 changes: 83 additions & 0 deletions tests/project/test_all_exports_unit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# -*- coding: utf-8 -*-
"""``pcapkit.all.__all__`` lists every name its star-imported packages export.

``pcapkit/all.py`` keeps its :attr:`__all__` by hand, so a name added to one of
the packages it aggregates is silently left out of ``from pcapkit.all import *``
(#1136). The aggregated packages are read from the ``from pcapkit.X import *``
statements in the source with :mod:`ast`, so a package added there is covered
without editing this module.

"""
from __future__ import annotations

import ast
import importlib
import importlib.util
import os
import unittest

RUNTIME_DEPS = ('tbtrim', 'aenum', 'chardet', 'dictdumper')
HAS_RUNTIME = all(importlib.util.find_spec(name) is not None for name in RUNTIME_DEPS)

ALL_PY = os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, 'pcapkit', 'all.py')

#: Names an aggregated package exports and :mod:`pcapkit.all` deliberately does
#: not list, keyed by the package. :mod:`pcapkit.utilities` has been unlisted
#: since ``769a17c78`` (2022-05-29), which commented its group out: its exports
#: are generic helpers -- ``warn``, ``reset``, ``configure``, ``detect`` -- that
#: ``import *`` should not drop into the caller's namespace. A name added here is
#: an API decision.
DELIBERATE_NON_EXPORTS = {
'pcapkit.utilities': ('logger', 'get_logger', 'configure', 'reset', 'ensure_output',
'warn', 'stacklevel', 'detect', 'beholder', 'prepare', 'seekset'),
}


def star_imported() -> 'list[str]':
"""Return the packages ``pcapkit/all.py`` imports with ``from ... import *``."""
with open(ALL_PY, encoding='utf-8') as file:
tree = ast.parse(file.read())
return [node.module for node in tree.body
if isinstance(node, ast.ImportFrom) and node.module is not None
and any(alias.name == '*' for alias in node.names)]


@unittest.skipUnless(HAS_RUNTIME, 'runtime dependencies not installed')
class AllExportsTests(unittest.TestCase):
"""``pcapkit.all.__all__`` keeps in step with the packages it aggregates."""

def test_star_imports_are_found(self) -> None:
packages = star_imported()
self.assertIn('pcapkit.protocols', packages)
self.assertIn('pcapkit.corekit', packages)
self.assertIn('pcapkit.foundation', packages)

def test_every_aggregated_export_is_listed_or_excluded(self) -> None:
import pcapkit.all

listed = set(pcapkit.all.__all__)
for package in star_imported():
with self.subTest(package=package):
exported = importlib.import_module(package).__all__
excluded = set(DELIBERATE_NON_EXPORTS.get(package, ()))
self.assertEqual(sorted(set(exported) - listed - excluded), [])

def test_exclusions_are_not_stale(self) -> None:
import pcapkit.all

for package, names in DELIBERATE_NON_EXPORTS.items():
with self.subTest(package=package):
self.assertIn(package, star_imported())
exported = set(importlib.import_module(package).__all__)
self.assertEqual(sorted(set(names) - exported), [])
self.assertEqual(sorted(set(names) & set(pcapkit.all.__all__)), [])

def test_every_listed_name_resolves(self) -> None:
import pcapkit.all

self.assertEqual([name for name in pcapkit.all.__all__
if not hasattr(pcapkit.all, name)], [])


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