Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-pyshark for openSUSE:Factory checked in at 2022-06-18 22:06:36 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pyshark (Old) and /work/SRC/openSUSE:Factory/.python-pyshark.new.1548 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pyshark" Sat Jun 18 22:06:36 2022 rev:3 rq:983629 version:0.4.6 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pyshark/python-pyshark.changes 2021-03-11 20:12:32.456687817 +0100 +++ /work/SRC/openSUSE:Factory/.python-pyshark.new.1548/python-pyshark.changes 2022-06-18 22:06:39.807696481 +0200 @@ -1,0 +2,11 @@ +Sat Jun 18 17:40:17 UTC 2022 - Martin Hauke <[email protected]> + +- Update to version 0.4.6 + Changes + * Added type hints for major classes. + * Edited docs to comply with PEP8. + Fixes + * Python 3.10 and Python 3.9 compatibility. + * Fixed crashed related to LiveRingCapture. + +------------------------------------------------------------------- Old: ---- v0.4.3.tar.gz New: ---- v0.4.6.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pyshark.spec ++++++ --- /var/tmp/diff_new_pack.nokFa0/_old 2022-06-18 22:06:40.335697231 +0200 +++ /var/tmp/diff_new_pack.nokFa0/_new 2022-06-18 22:06:40.355697259 +0200 @@ -1,8 +1,8 @@ # # spec file for package python-pyshark # -# Copyright (c) 2021 SUSE LLC -# Copyright (c) 2019-2021, Martin Hauke <[email protected]> +# Copyright (c) 2022 SUSE LLC +# Copyright (c) 2019-2022, Martin Hauke <[email protected]> # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -20,7 +20,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-pyshark -Version: 0.4.3 +Version: 0.4.6 Release: 0 Summary: A Python wrapper for tshark output parsing License: MIT ++++++ v0.4.3.tar.gz -> v0.4.6.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyshark-0.4.3/.travis.yml new/pyshark-0.4.6/.travis.yml --- old/pyshark-0.4.3/.travis.yml 2021-02-06 08:44:25.000000000 +0100 +++ new/pyshark-0.4.6/.travis.yml 2022-06-07 12:55:49.000000000 +0200 @@ -1,10 +1,12 @@ -dist: bionic +dist: bionic language: python python: - "3.5" - "3.6" - "3.7" - "3.8" + - "3.9" + - "3.10" before_install: - sudo apt-get update -qq - sudo apt-get install -y tshark diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyshark-0.4.3/README.md new/pyshark-0.4.6/README.md --- old/pyshark-0.4.3/README.md 2021-02-06 08:44:25.000000000 +0100 +++ new/pyshark-0.4.6/README.md 2022-06-07 12:55:49.000000000 +0200 @@ -9,7 +9,7 @@ **Python2 deprecation** - This package no longer supports Python2. If you wish to still use it in Python2, you can: * Use version 0.3.8 * Install pyshark-legacy via pypi -* Clone the pyshark-legacy [repo (https://github.com/KimiNewt/pyshark-legacy)], where bugfixes will be applied. +* Clone the pyshark-legacy [repo](https://github.com/KimiNewt/pyshark-legacy), where bugfixes will be applied. **Looking for contributors** - for various reasons I have a hard time finding time to maintain and enhance the package at the moment. Any pull-requests will be reviewed and if any one is interested and is suitable, I will be happy to include them in the project. Feel free to mail me at dorgreen1 at gmail. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyshark-0.4.3/requirements.txt new/pyshark-0.4.6/requirements.txt --- old/pyshark-0.4.3/requirements.txt 2021-02-06 08:44:25.000000000 +0100 +++ new/pyshark-0.4.6/requirements.txt 2022-06-07 12:55:49.000000000 +0200 @@ -2,4 +2,5 @@ py pytest mock -lxml \ No newline at end of file +lxml +packaging \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyshark-0.4.3/src/pyshark/capture/capture.py new/pyshark-0.4.6/src/pyshark/capture/capture.py --- old/pyshark-0.4.3/src/pyshark/capture/capture.py 2021-02-06 08:44:25.000000000 +0100 +++ new/pyshark-0.4.6/src/pyshark/capture/capture.py 2022-06-07 12:55:49.000000000 +0200 @@ -5,8 +5,9 @@ import concurrent.futures import sys import logging -from distutils.version import LooseVersion +from packaging import version +from pyshark.packet.packet import Packet from pyshark.tshark.tshark import get_process_path, get_tshark_display_filter_flag, \ tshark_supports_json, TSharkVersionException, get_tshark_version, tshark_supports_duplicate_keys from pyshark.tshark.tshark_json import packet_from_json_packet @@ -18,6 +19,7 @@ else: asyncTimeoutError = asyncio.exceptions.TimeoutError + class TSharkCrashException(Exception): pass @@ -35,7 +37,7 @@ pass -class Capture(object): +class Capture: """Base class for packet captures.""" DEFAULT_BATCH_SIZE = 2 ** 16 SUMMARIES_BATCH_SIZE = 64 @@ -64,14 +66,16 @@ self._decode_as = decode_as self._disable_protocol = disable_protocol self._json_has_duplicate_keys = True - self._log = logging.Logger(self.__class__.__name__, level=self.DEFAULT_LOG_LEVEL) + self._log = logging.Logger( + self.__class__.__name__, level=self.DEFAULT_LOG_LEVEL) self._closed = False self._custom_parameters = custom_parameters self._eof_reached = False self.__tshark_version = None if include_raw and not use_json: - raise RawMustUseJsonException("use_json must be True if include_raw") + raise RawMustUseJsonException( + "use_json must be True if include_raw") if self.debug: self.set_debug() @@ -96,12 +100,12 @@ def __len__(self): return len(self._packets) - def next(self): + def next(self) -> Packet: return self.next_packet() # Allows for child classes to call next() from super() without 2to3 "fixing" # the call - def next_packet(self): + def next_packet(self) -> Packet: if self._current_packet >= len(self._packets): raise StopIteration() cur_packet = self._packets[self._current_packet] @@ -134,7 +138,8 @@ raise StopCapture() try: - self.apply_on_packets(keep_packet, timeout=timeout, packet_count=packet_count) + self.apply_on_packets( + keep_packet, timeout=timeout, packet_count=packet_count) self.loaded = True except asyncTimeoutError: pass @@ -143,7 +148,8 @@ """Sets the capture to debug mode (or turns it off if specified).""" if set_to: handler = logging.StreamHandler(sys.stdout) - handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")) + handler.setFormatter(logging.Formatter( + "%(asctime)s - %(name)s - %(levelname)s - %(message)s")) self._log.addHandler(handler) self._log.level = log_level self.debug = set_to @@ -154,7 +160,7 @@ self.eventloop = asyncio.ProactorEventLoop() else: try: - self.eventloop = asyncio.get_event_loop() + self.eventloop = asyncio.get_event_loop_policy().get_event_loop() except RuntimeError: if threading.current_thread() != threading.main_thread(): # Ran not in main thread, make a new eventloop @@ -162,8 +168,17 @@ asyncio.set_event_loop(self.eventloop) else: raise - if os.name == "posix" and isinstance(threading.current_thread(), threading._MainThread): - asyncio.get_child_watcher().attach_loop(self.eventloop) + if os.name == "posix" and isinstance(threading.current_thread(), threading._MainThread): + # The default child watchers (ThreadedChildWatcher) attach_loop method is empty! + # While using pyshark with ThreadedChildWatcher, asyncio could raise a ChildProcessError + # "Unknown child process pid %d, will report returncode 255" + # This led to a TSharkCrashException in _cleanup_subprocess. + # Using the SafeChildWatcher fixes this issue, but it is slower. + # SafeChildWatcher O(n) -> large numbers of processes are slow + # ThreadedChildWatcher O(1) -> independent of process number + # asyncio.get_child_watcher().attach_loop(self.eventloop) + asyncio.set_child_watcher(asyncio.SafeChildWatcher()) + asyncio.get_child_watcher().attach_loop(self.eventloop) def _get_json_separators(self): """"Returns the separators between packets in a JSON output @@ -172,9 +187,9 @@ The latter variable being the number of characters to ignore in order to pass the packet (i.e. extra newlines, commas, parenthesis). """ - if self._get_tshark_version() >= LooseVersion("3.0.0"): + if self._get_tshark_version() >= version.parse("3.0.0"): return ("%s },%s" % (os.linesep, os.linesep)).encode(), ("}%s]" % os.linesep).encode(), ( - 1 + len(os.linesep)) + 1 + len(os.linesep)) else: return ("}%s%s ," % (os.linesep, os.linesep)).encode(), ("}%s%s]" % (os.linesep, os.linesep)).encode(), 1 @@ -229,8 +244,10 @@ :param packet_count: If given, stops after this amount of packets is captured. """ # NOTE: This has code duplication with the async version, think about how to solve this - tshark_process = existing_process or self.eventloop.run_until_complete(self._get_tshark_process()) - psml_structure, data = self.eventloop.run_until_complete(self._get_psml_struct(tshark_process.stdout)) + tshark_process = existing_process or self.eventloop.run_until_complete( + self._get_tshark_process()) + psml_structure, data = self.eventloop.run_until_complete( + self._get_psml_struct(tshark_process.stdout)) packets_captured = 0 data = b"" @@ -253,11 +270,12 @@ break finally: if tshark_process in self._running_processes: - self.eventloop.run_until_complete(self._cleanup_subprocess(tshark_process)) + self.eventloop.run_until_complete( + self._cleanup_subprocess(tshark_process)) def apply_on_packets(self, callback, timeout=None, packet_count=None): """Runs through all packets and calls the given callback (a function) with each one as it is read. - + If the capture is infinite (i.e. a live capture), it will run forever, otherwise it will complete after all packets have been read. @@ -330,7 +348,8 @@ while not psml_struct: new_data = await fd.read(self.SUMMARIES_BATCH_SIZE) data += new_data - psml_struct, data = self._extract_tag_from_data(data, b"structure") + psml_struct, data = self._extract_tag_from_data( + data, b"structure") if psml_struct: psml_struct = psml_structure_from_xml(psml_struct) elif not new_data: @@ -355,9 +374,11 @@ if packet: if self.use_json: - packet = packet_from_json_packet(packet, deduplicate_fields=self._json_has_duplicate_keys) + packet = packet_from_json_packet( + packet, deduplicate_fields=self._json_has_duplicate_keys) else: - packet = packet_from_xml_packet(packet, psml_structure=psml_structure) + packet = packet_from_xml_packet( + packet, psml_structure=psml_structure) return packet, existing_data new_data = await stream.read(self.DEFAULT_BATCH_SIZE) @@ -387,7 +408,8 @@ if self.use_json: output_type = "json" if not tshark_supports_json(self._get_tshark_version()): - raise TSharkVersionException("JSON only supported on Wireshark >= 2.2.0") + raise TSharkVersionException( + "JSON only supported on Wireshark >= 2.2.0") if tshark_supports_duplicate_keys(self._get_tshark_version()): output_parameters.append("--no-duplicate-keys") self._json_has_duplicate_keys = False @@ -396,7 +418,8 @@ parameters = [self._get_tshark_path(), "-l", "-n", "-T", output_type] + \ self.get_parameters(packet_count=packet_count) + output_parameters - self._log.debug("Creating TShark subprocess with parameters: " + " ".join(parameters)) + self._log.debug( + "Creating TShark subprocess with parameters: " + " ".join(parameters)) self._log.debug("Executable: %s" % parameters[0]) tshark_process = await asyncio.create_subprocess_exec(*parameters, stdout=subprocess.PIPE, @@ -406,7 +429,8 @@ return tshark_process def _created_new_process(self, parameters, process, process_name="TShark"): - self._log.debug(process_name + " subprocess created") + self._log.debug( + process_name + f" subprocess (pid {process.pid}) created") if process.returncode is not None and process.returncode != 0: raise TSharkCrashException( "%s seems to have crashed. Try updating it. (command ran: '%s')" % ( @@ -415,12 +439,14 @@ async def _cleanup_subprocess(self, process): """Kill the given process and properly closes any pipes connected to it.""" + self._log.debug(f"Cleanup Subprocess (pid {process.pid})") if process.returncode is None: try: process.kill() return await asyncio.wait_for(process.wait(), 1) except asyncTimeoutError: - self._log.debug("Waiting for process to close failed, may have zombie process.") + self._log.debug( + "Waiting for process to close failed, may have zombie process.") except ProcessLookupError: pass except OSError: @@ -428,12 +454,11 @@ raise elif process.returncode > 0: if process.returncode != 1 or self._eof_reached: - raise TSharkCrashException("TShark seems to have crashed (retcode: %d). " - "Try rerunning in debug mode [ capture_obj.set_debug() ] or try updating tshark." - % process.returncode) + raise TSharkCrashException(f"TShark (pid {process.pid}) seems to have crashed (retcode: {process.returncode}). " + "Try rerunning in debug mode [ capture_obj.set_debug() ] or try updating tshark.") def close(self): - self.eventloop.run_until_complete(self.close_async()) + self.eventloop.create_task(self.close_async()) async def close_async(self): for process in self._running_processes.copy(): @@ -447,7 +472,9 @@ def __enter__(self): return self async def __aenter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self.close() - async def __aexit__(self, exc_type, exc_val, exc_tb): await self.close_async() + + async def __aexit__(self, exc_type, exc_val, + exc_tb): await self.close_async() def get_parameters(self, packet_count=None): """Returns the special tshark parameters to be used according to the configuration of this class.""" @@ -479,14 +506,16 @@ for preference_name, preference_value in self._override_prefs.items(): if all(self.encryption) and preference_name in ("wlan.enable_decryption", "uat:80211_keys"): continue # skip if override preferences also given via --encryption options - params += ["-o", "{0}:{1}".format(preference_name, preference_value)] + params += ["-o", + "{0}:{1}".format(preference_name, preference_value)] if self._output_file: params += ["-w", self._output_file] if self._decode_as: for criterion, decode_as_proto in self._decode_as.items(): - params += ["-d", ",".join([criterion.strip(), decode_as_proto.strip()])] + params += ["-d", + ",".join([criterion.strip(), decode_as_proto.strip()])] if self._disable_protocol: params += ["--disable-protocol", self._disable_protocol.strip()] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyshark-0.4.3/src/pyshark/capture/file_capture.py new/pyshark-0.4.6/src/pyshark/capture/file_capture.py --- old/pyshark-0.4.3/src/pyshark/capture/file_capture.py 2021-02-06 08:44:25.000000000 +0100 +++ new/pyshark-0.4.6/src/pyshark/capture/file_capture.py 2022-06-07 12:55:49.000000000 +0200 @@ -1,6 +1,7 @@ import os from pyshark.capture.capture import Capture +from pyshark.packet.packet import Packet class FileCapture(Capture): @@ -50,7 +51,7 @@ self.keep_packets = keep_packets self._packet_generator = self._packets_from_tshark_sync() - def next(self): + def next(self) -> Packet: """Returns the next packet in the cap. If the capture's keep_packets flag is True, will also keep it in the internal packet list. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyshark-0.4.3/src/pyshark/capture/inmem_capture.py new/pyshark-0.4.6/src/pyshark/capture/inmem_capture.py --- old/pyshark-0.4.3/src/pyshark/capture/inmem_capture.py 2021-02-06 08:44:25.000000000 +0100 +++ new/pyshark-0.4.6/src/pyshark/capture/inmem_capture.py 2022-06-07 12:55:49.000000000 +0200 @@ -6,7 +6,7 @@ import struct import time import warnings -from distutils.version import LooseVersion +from packaging import version from pyshark.capture.capture import Capture, StopCapture @@ -59,7 +59,8 @@ def get_parameters(self, packet_count=None): """Returns the special tshark parameters to be used according to the configuration of this class.""" - params = super(InMemCapture, self).get_parameters(packet_count=packet_count) + params = super(InMemCapture, self).get_parameters( + packet_count=packet_count) params += ['-i', '-'] return params @@ -70,7 +71,8 @@ self._current_tshark = proc # Create PCAP header - header = struct.pack("IHHIIII", 0xa1b2c3d4, 2, 4, 0, 0, 0x7fff, self._current_linktype) + header = struct.pack("IHHIIII", 0xa1b2c3d4, 2, 4, + 0, 0, 0x7fff, self._current_linktype) proc.stdin.write(header) return proc @@ -82,7 +84,7 @@ The latter variable being the number of characters to ignore in order to pass the packet (i.e. extra newlines, commas, parenthesis). """ - if self._get_tshark_version() >= LooseVersion("2.6.7"): + if self._get_tshark_version() >= version.parse("2.6.7"): return ("%s }" % os.linesep).encode(), ("}%s]" % os.linesep).encode(), 0 else: return ("}%s%s" % (os.linesep, os.linesep)).encode(), ("}%s%s]" % (os.linesep, os.linesep)).encode(), 1 @@ -97,7 +99,8 @@ secs = int(now) usecs = int((now * 1000000) % 1000000) # Write packet header - self._current_tshark.stdin.write(struct.pack("IIII", secs, usecs, len(packet), len(packet))) + self._current_tshark.stdin.write(struct.pack( + "IIII", secs, usecs, len(packet), len(packet))) self._current_tshark.stdin.write(packet) def parse_packet(self, binary_packet, sniff_time=None, timeout=DEFAULT_TIMEOUT): @@ -117,7 +120,9 @@ DOES NOT CLOSE tshark. It must be closed manually by calling close() when you're done working with it. """ - return asyncio.get_event_loop().run_until_complete(self.parse_packets_async(binary_packets, sniff_times, timeout)) + if self.eventloop is None: + self._setup_eventloop() + return self.eventloop.run_until_complete(self.parse_packets_async(binary_packets, sniff_times, timeout)) async def parse_packets_async(self, binary_packets, sniff_times=None, timeout=DEFAULT_TIMEOUT): """A coroutine which parses binary packets and return a list of parsed packets. @@ -170,7 +175,8 @@ By default, assumes the packet is an ethernet packet. For another link type, supply the linktype argument (most can be found in the class LinkTypes) """ - warnings.warn("Deprecated method. Use InMemCapture.parse_packet() instead.") + warnings.warn( + "Deprecated method. Use InMemCapture.parse_packet() instead.") self._current_linktype = linktype pkt = self.parse_packet(binary_packet, timeout=timeout) self.close() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyshark-0.4.3/src/pyshark/capture/live_capture.py new/pyshark-0.4.6/src/pyshark/capture/live_capture.py --- old/pyshark-0.4.3/src/pyshark/capture/live_capture.py 2021-02-06 08:44:25.000000000 +0100 +++ new/pyshark-0.4.6/src/pyshark/capture/live_capture.py 2022-06-07 12:55:49.000000000 +0200 @@ -1,7 +1,6 @@ import os import asyncio -import sys -from distutils.version import LooseVersion +from packaging import version from pyshark.capture.capture import Capture from pyshark.tshark.tshark import get_tshark_interfaces, get_process_path @@ -46,9 +45,6 @@ self.bpf_filter = bpf_filter self.monitor_mode = monitor_mode - if sys.platform == "win32" and monitor_mode: - raise WindowsError("Monitor mode is not supported by the Windows platform") - if interface is None: self.interfaces = get_tshark_interfaces(tshark_path) elif isinstance(interface, str): @@ -57,18 +53,16 @@ self.interfaces = interface def get_parameters(self, packet_count=None): - """ - Returns the special tshark parameters to be used according to the configuration of this class. - """ + """Returns the special tshark parameters to be used according to the configuration of this class.""" params = super(LiveCapture, self).get_parameters(packet_count=packet_count) # Read from STDIN - params += ["-r", "-"] + params += ["-i", "-"] return params def _get_dumpcap_parameters(self): # Don't report packet counts. params = ["-q"] - if self._get_tshark_version() < LooseVersion("2.5.0"): + if self._get_tshark_version() < version.parse("2.5.0"): # Tshark versions older than 2.5 don't support pcapng. This flag forces dumpcap to output pcap. params += ["-P"] if self.bpf_filter: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyshark-0.4.3/src/pyshark/capture/live_ring_capture.py new/pyshark-0.4.6/src/pyshark/capture/live_ring_capture.py --- old/pyshark-0.4.3/src/pyshark/capture/live_ring_capture.py 2021-02-06 08:44:25.000000000 +0100 +++ new/pyshark-0.4.6/src/pyshark/capture/live_ring_capture.py 2022-06-07 12:55:49.000000000 +0200 @@ -2,9 +2,7 @@ class LiveRingCapture(LiveCapture): - """ - Represents a live ringbuffer capture on a network interface. - """ + """Represents a live ringbuffer capture on a network interface.""" def __init__(self, ring_file_size=1024, num_ring_files=1, ring_file_name='/tmp/pyshark.pcap', interface=None, bpf_filter=None, display_filter=None, only_summaries=False, decryption_key=None, @@ -42,9 +40,7 @@ self.ring_file_name = ring_file_name def get_parameters(self, packet_count=None): - """ - Returns the special tshark parameters to be used according to the configuration of this class. - """ params = super(LiveRingCapture, self).get_parameters(packet_count=packet_count) - params += ['-b', 'filesize:' + str(self.ring_file_size), '-b', 'files:' + str(self.num_ring_files), '-w', self.ring_file_name, '-P'] + params += ['-b', 'filesize:' + str(self.ring_file_size), '-b', 'files:' + str(self.num_ring_files), + '-w', self.ring_file_name, '-P'] return params diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyshark-0.4.3/src/pyshark/capture/pipe_capture.py new/pyshark-0.4.6/src/pyshark/capture/pipe_capture.py --- old/pyshark-0.4.3/src/pyshark/capture/pipe_capture.py 2021-02-06 08:44:25.000000000 +0100 +++ new/pyshark-0.4.6/src/pyshark/capture/pipe_capture.py 2022-06-07 12:55:49.000000000 +0200 @@ -1,13 +1,14 @@ -from pyshark.capture.capture import Capture import os +from pyshark.capture.capture import Capture + + class PipeCapture(Capture): def __init__(self, pipe, display_filter=None, only_summaries=False, decryption_key=None, encryption_type='wpa-pwk', decode_as=None, - disable_protocol=None, tshark_path=None, override_prefs=None, use_json=False, include_raw=False, - eventloop=None, custom_parameters=None, debug=False): - """ - Receives a file-like and reads the packets from there (pcap format). + disable_protocol=None, tshark_path=None, override_prefs=None, use_json=False, + include_raw=False, eventloop=None, custom_parameters=None, debug=False): + """Receives a file-like and reads the packets from there (pcap format). :param bpf_filter: BPF filter to use on packets. :param display_filter: Display (wireshark) filter to use. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyshark-0.4.3/src/pyshark/capture/remote_capture.py new/pyshark-0.4.6/src/pyshark/capture/remote_capture.py --- old/pyshark-0.4.3/src/pyshark/capture/remote_capture.py 2021-02-06 08:44:25.000000000 +0100 +++ new/pyshark-0.4.6/src/pyshark/capture/remote_capture.py 2022-06-07 12:55:49.000000000 +0200 @@ -2,9 +2,7 @@ class RemoteCapture(LiveCapture): - """ - A capture which is performed on a remote machine which has an rpcapd service running. - """ + """A capture which is performed on a remote machine which has an rpcapd service running.""" def __init__(self, remote_host, remote_interface, remote_port=2002, bpf_filter=None, only_summaries=False, decryption_key=None, encryption_type='wpa-pwk', decode_as=None, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyshark-0.4.3/src/pyshark/config.py new/pyshark-0.4.6/src/pyshark/config.py --- old/pyshark-0.4.3/src/pyshark/config.py 2021-02-06 08:44:25.000000000 +0100 +++ new/pyshark-0.4.6/src/pyshark/config.py 2022-06-07 12:55:49.000000000 +0200 @@ -1,6 +1,6 @@ import os -import py +from configparser import ConfigParser import pyshark @@ -8,4 +8,6 @@ def get_config(): - return py.iniconfig.IniConfig(CONFIG_PATH) + config = ConfigParser() + config.read(CONFIG_PATH) + return config diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyshark-0.4.3/src/pyshark/packet/fields.py new/pyshark-0.4.6/src/pyshark/packet/fields.py --- old/pyshark-0.4.3/src/pyshark/packet/fields.py 2021-02-06 08:44:25.000000000 +0100 +++ new/pyshark-0.4.6/src/pyshark/packet/fields.py 2022-06-07 12:55:49.000000000 +0200 @@ -1,12 +1,12 @@ import binascii +import typing from pyshark.packet.common import Pickleable, SlotsPickleable class LayerField(SlotsPickleable): - """ - Holds all data about a field of a layer, both its actual value and its name and nice representation. - """ + """Holds all data about a field of a layer, both its actual value and its name and nice representation.""" + # Note: We use this object with slots and not just a dict because # it's much more memory-efficient (cuts about a third of the memory). __slots__ = ['name', 'showname', 'raw_value', 'show', 'hide', 'pos', 'size', 'unmaskedvalue'] @@ -28,10 +28,8 @@ def __repr__(self): return '<LayerField %s: %s>' % (self.name, self.get_default_value()) - def get_default_value(self): - """ - Gets the best 'value' string this field has. - """ + def get_default_value(self) -> str: + """Gets the best 'value' string this field has.""" val = self.show if not val: val = self.raw_value @@ -40,23 +38,22 @@ return val @property - def showname_value(self): - """ - For fields which do not contain a normal value, we attempt to take their value from the showname. - """ + def showname_value(self) -> typing.Union[str, None]: + """The "pretty value" (as displayed by Wireshark) of the field.""" if self.showname and ': ' in self.showname: return self.showname.split(': ', 1)[1] + return None @property - def showname_key(self): + def showname_key(self) -> typing.Union[str, None]: + """The "pretty name" (as displayed by Wireshark) of the field.""" if self.showname and ': ' in self.showname: return self.showname.split(': ', 1)[0] + return None @property - def binary_value(self): - """ - Converts this field to binary (assuming it's a binary string) - """ + def binary_value(self) -> bytes: + """Converts this field to binary (assuming it's a binary string)""" str_raw_value = str(self.raw_value) if len(str_raw_value) % 2 == 1: str_raw_value = '0' + str_raw_value @@ -64,17 +61,15 @@ return binascii.unhexlify(str_raw_value) @property - def int_value(self): - """ - Returns the int value of this field (assuming it's an integer integer). - """ + def int_value(self) -> int: + """Returns the int value of this field (assuming it's represented as a decimal integer).""" return int(self.raw_value) @property - def hex_value(self): - """ - Returns the int value of this field if it's in base 16 (either as a normal number or in - a "0xFFFF"-style hex value) + def hex_value(self) -> int: + """Returns the int value of this field if it's in base 16 + + (either as a normal number or in a "0xFFFF"-style hex value) """ return int(self.raw_value, 16) @@ -82,8 +77,8 @@ class LayerFieldsContainer(str, Pickleable): - """ - An object which contains one or more fields (of the same name). + """An object which contains one or more fields (of the same name). + When accessing member, such as showname, raw_value, etc. the appropriate member of the main (first) field saved in this container will be shown. """ @@ -103,22 +98,18 @@ self.fields.append(field) @property + def all_fields(self): + """Returns all fields in a list, the main field followed by the alternate fields.""" + return self.fields + + @property def main_field(self): return self.fields[0] @property def alternate_fields(self): - """ - Return the alternate values of this field containers (non-main ones). - """ + """Return the alternate values of this field containers (non-main ones).""" return self.fields[1:] - @property - def all_fields(self): - """ - Returns all fields in a list, the main field followed by the alternate fields. - """ - return self.fields - def __getattr__(self, item): return getattr(self.main_field, item) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyshark-0.4.3/src/pyshark/packet/layer.py new/pyshark-0.4.6/src/pyshark/packet/layer.py --- old/pyshark-0.4.3/src/pyshark/packet/layer.py 2021-02-06 08:44:25.000000000 +0100 +++ new/pyshark-0.4.6/src/pyshark/packet/layer.py 2022-06-07 12:55:49.000000000 +0200 @@ -1,4 +1,5 @@ import os +import typing import py @@ -7,9 +8,7 @@ class Layer(Pickleable): - """ - An object representing a Packet layer. - """ + """An object representing a Packet layer.""" DATA_LAYER = 'data' def __init__(self, xml_obj=None, raw_mode=False): @@ -30,7 +29,7 @@ else: self._all_fields[attributes['name']] = LayerFieldsContainer(field_obj) - def __getattr__(self, item): + def __getattr__(self, item) -> LayerFieldsContainer: val = self.get_field(item) if val is None: raise AttributeError() @@ -39,9 +38,9 @@ return val def get(self, item, default=None): - """ - Works the same way as getattr, but returns the given default if not the field was not found - """ + """Gets a field in the layer, or the default if not found. + + Works the same way as getattr, but returns the given default if not the field was not found""" try: return getattr(self, item) except AttributeError: @@ -50,10 +49,8 @@ def __dir__(self): return dir(type(self)) + list(self.__dict__.keys()) + self.field_names - def get_field(self, name): - """ - Gets the XML field object of the given name. - """ + def get_field(self, name) -> LayerFieldsContainer: + """Gets the XML field object of the given name.""" # Quicker in case the exact name was used. field = self._all_fields.get(name) if field is not None: @@ -63,10 +60,11 @@ if self._sanitize_field_name(name) == self._sanitize_field_name(field_name): return field - def get_field_value(self, name, raw=False): - """ - Tries getting the value of the given field. - Tries it in the following order: show (standard nice display), value (raw value), showname (extended nice display). + def get_field_value(self, name, raw=False) -> typing.Union[LayerFieldsContainer, None]: + """Tries getting the value of the given field. + + Tries it in the following order: show (standard nice display), value (raw value), + showname (extended nice display). :param name: The name of the field :param raw: Only return raw value @@ -74,7 +72,7 @@ """ field = self.get_field(name) if field is None: - return + return None if raw: return field.raw_value @@ -82,20 +80,15 @@ return field @property - def _field_prefix(self): - """ - Prefix to field names in the XML. - """ + def _field_prefix(self) -> str: + """Prefix to field names in the XML.""" if self.layer_name == 'geninfo': return '' return self.layer_name + '.' - + @property - def field_names(self): - """ - Gets all XML field names of this layer. - :return: list of strings - """ + def field_names(self) -> typing.List[str]: + """Gets all XML field names of this layer.""" return [self._sanitize_field_name(field_name) for field_name in self._all_fields] @@ -106,8 +99,9 @@ return self._layer_name def _sanitize_field_name(self, field_name): - """ - Sanitizes an XML field name (since it might have characters which would make it inaccessible as a python attribute). + """Sanitizes an XML field name + + An xml field might have characters which would make it inaccessible as a python attribute). """ field_name = field_name.replace(self._field_prefix, '') return field_name.replace('.', '_').replace('-', '_').lower() @@ -144,9 +138,7 @@ return all_fields def _get_all_field_lines(self): - """ - Returns all lines that represent the fields of the layer (both their names and values). - """ + """Returns all lines that represent the fields of the layer (both their names and values).""" for field in self._get_all_fields_with_alternates(): # Change to yield from for line in self._get_field_or_layer_repr(field): @@ -177,18 +169,19 @@ elif field.raw_value: return "%s: %s" % (self._sanitize_field_name(field.name), field.raw_value) - def get_field_by_showname(self, showname): - """ - Gets a field by its "showname" - (the name that appears in Wireshark's detailed display i.e. in 'User-Agent: Mozilla...', 'User-Agent' is the - showname) + def get_field_by_showname(self, showname) -> typing.Union[LayerFieldsContainer, None]: + """Gets a field by its "showname" + + This is the name that appears in Wireshark's detailed display i.e. in 'User-Agent: Mozilla...', + 'User-Agent' is the .showname - Returns None if not found. + Returns None if not found. """ for field in self._get_all_fields_with_alternates(): if field.showname_key == showname: # Return it if "XXX: whatever == XXX" return field + return None class JsonLayer(Layer): @@ -338,9 +331,9 @@ return LayerFieldsContainer(LayerField(name=name, value=field)) - def has_field(self, dotted_name): - """ - Checks whether the layer has the given field name. + def has_field(self, dotted_name) -> bool: + """Checks whether the layer has the given field name. + Can get a dotted name, i.e. layer.sublayer.subsublayer.field """ parts = dotted_name.split('.') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyshark-0.4.3/src/pyshark/packet/packet.py new/pyshark-0.4.6/src/pyshark/packet/packet.py --- old/pyshark-0.4.3/src/pyshark/packet/packet.py 2021-02-06 08:44:25.000000000 +0100 +++ new/pyshark-0.4.6/src/pyshark/packet/packet.py 2022-06-07 12:55:49.000000000 +0200 @@ -1,14 +1,16 @@ import datetime import os import binascii +import typing from pyshark.packet import consts from pyshark.packet.common import Pickleable +from pyshark.packet.layer import Layer class Packet(Pickleable): - """ - A packet object which contains layers. + """A packet object which contains layers. + Layers can be accessed via index or name. """ @@ -50,8 +52,7 @@ raise KeyError('Layer does not exist in packet') def __contains__(self, item): - """ - Checks if the layer is inside the packet. + """Checks if the layer is inside the packet. :param item: name of the layer """ @@ -64,7 +65,7 @@ def __dir__(self): return dir(type(self)) + list(self.__dict__.keys()) + [l.layer_name for l in self.layers] - def get_raw_packet(self): + def get_raw_packet(self) -> bytes: assert "FRAME_RAW" in self, "Packet contains no raw data. In order to contains it, " \ "make sure that use_json and include_raw are set to True " \ "in the Capture object" @@ -81,7 +82,7 @@ return True @property - def sniff_time(self): + def sniff_time(self) -> datetime.datetime: try: timestamp = float(self.sniff_timestamp) except ValueError: @@ -105,10 +106,8 @@ @property def _packet_string(self): - """ - A simple pretty string that represents the packet. - """ - return 'Packet (Length: %s)%s' %(self.length, os.linesep) + """A simple pretty string that represents the packet.""" + return 'Packet (Length: %s)%s' % (self.length, os.linesep) def pretty_print(self): for layer in self.layers: @@ -126,19 +125,19 @@ raise AttributeError("No attribute named %s" % item) @property - def highest_layer(self): + def highest_layer(self) -> Layer: return self.layers[-1].layer_name.upper() @property - def transport_layer(self): + def transport_layer(self) -> Layer: for layer in consts.TRANSPORT_LAYERS: if layer in self: return layer - def get_multiple_layers(self, layer_name): - """ - Returns a list of all the layers in the packet that are of the layer type (an incase-sensitive string). - This is in order to retrieve layers which appear multiple times in the same packet (i.e. double VLAN) which cannot be - retrieved by easier means. + def get_multiple_layers(self, layer_name) -> typing.List[Layer]: + """Returns a list of all the layers in the packet that are of the layer type (an incase-sensitive string). + + This is in order to retrieve layers which appear multiple times in the same packet (i.e. double VLAN) + which cannot be retrieved by easier means. """ - return [layer for layer in self.layers if layer.layer_name.lower() == layer_name.lower()] + return [layer for layer in self.layers if layer.layer_name.lower() == layer_name.lower()] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyshark-0.4.3/src/pyshark/packet/packet_summary.py new/pyshark-0.4.6/src/pyshark/packet/packet_summary.py --- old/pyshark-0.4.3/src/pyshark/packet/packet_summary.py 2021-02-06 08:44:25.000000000 +0100 +++ new/pyshark-0.4.6/src/pyshark/packet/packet_summary.py 2022-06-07 12:55:49.000000000 +0200 @@ -1,6 +1,6 @@ class PacketSummary(object): - """ - A simple object containing a psml summary. + """A simple object containing a psml summary. + Can contain various summary information about a packet. """ @@ -23,5 +23,5 @@ return self.summary_line @property - def summary_line(self): + def summary_line(self) -> str: return ' '.join([self._fields[key] for key in self._field_order]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyshark-0.4.3/src/pyshark/tshark/tshark.py new/pyshark-0.4.6/src/pyshark/tshark/tshark.py --- old/pyshark-0.4.3/src/pyshark/tshark/tshark.py 2021-02-06 08:44:25.000000000 +0100 +++ new/pyshark-0.4.6/src/pyshark/tshark/tshark.py 2022-06-07 12:55:49.000000000 +0200 @@ -1,5 +1,5 @@ """Module used for the actual running of TShark""" -from distutils.version import LooseVersion +from packaging import version import os import subprocess import sys @@ -67,26 +67,26 @@ version_output = subprocess.check_output(parameters, stderr=null).decode("ascii") version_line = version_output.splitlines()[0] - pattern = '.*\s(\d+\.\d+\.\d+).*' # match " #.#.#" version pattern + pattern = r'.*\s(\d+\.\d+\.\d+).*' # match " #.#.#" version pattern m = re.match(pattern, version_line) if not m: raise TSharkVersionException("Unable to parse TShark version from: {}".format(version_line)) version_string = m.groups()[0] # Use first match found - return LooseVersion(version_string) + return version.parse(version_string) def tshark_supports_duplicate_keys(tshark_version): - return tshark_version >= LooseVersion("2.6.7") + return tshark_version >= version.parse("2.6.7") def tshark_supports_json(tshark_version): - return tshark_version >= LooseVersion("2.2.0") + return tshark_version >= version.parse("2.2.0") def get_tshark_display_filter_flag(tshark_version): """Returns '-Y' for tshark versions >= 1.10.0 and '-R' for older versions.""" - if tshark_version >= LooseVersion("1.10.0"): + if tshark_version >= version.parse("1.10.0"): return "-Y" else: return "-R" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyshark-0.4.3/src/pyshark/tshark/tshark_xml.py new/pyshark-0.4.6/src/pyshark/tshark/tshark_xml.py --- old/pyshark-0.4.3/src/pyshark/tshark/tshark_xml.py 2021-02-06 08:44:25.000000000 +0100 +++ new/pyshark-0.4.6/src/pyshark/tshark/tshark_xml.py 2022-06-07 12:55:49.000000000 +0200 @@ -5,6 +5,10 @@ from pyshark.packet.packet import Packet from pyshark.packet.packet_summary import PacketSummary +# Prepare dictionary used with str.translate for removing invalid XML characters +DEL_BAD_XML_CHARS = {bad_char: None for bad_char in range(0x00, 0x20) if not bad_char in (0x09, 0x0a, 0x0d)} +DEL_BAD_XML_CHARS.update({bad_char: None for bad_char in range(0xd800, 0xe000)}) +DEL_BAD_XML_CHARS.update({bad_char: None for bad_char in range(0xfffe, 0x10000)}) def psml_structure_from_xml(psml_structure): if not isinstance(psml_structure, lxml.objectify.ObjectifiedElement): @@ -23,6 +27,7 @@ """ if not isinstance(xml_pkt, lxml.objectify.ObjectifiedElement): parser = lxml.objectify.makeparser(huge_tree=True, recover=True) + xml_pkt = xml_pkt.decode(errors='ignore').translate(DEL_BAD_XML_CHARS) xml_pkt = lxml.objectify.fromstring(xml_pkt, parser) if psml_structure: return _packet_from_psml_packet(xml_pkt, psml_structure) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyshark-0.4.3/src/setup.py new/pyshark-0.4.6/src/setup.py --- old/pyshark-0.4.3/src/setup.py 2021-02-06 08:44:25.000000000 +0100 +++ new/pyshark-0.4.6/src/setup.py 2022-06-07 12:55:49.000000000 +0200 @@ -6,12 +6,13 @@ setup( name="pyshark", - version="0.4.3", + version="0.4.6", packages=find_packages(), package_data={'': ['*.ini', '*.pcapng']}, - install_requires=['lxml', 'py'], + install_requires=['lxml', 'py', 'packaging'], tests_require=['pytest'], url="https://github.com/KimiNewt/pyshark", + license="MIT", long_description=long_description, author="KimiNewt", description="Python wrapper for tshark, allowing python packet parsing using wireshark dissectors", @@ -23,5 +24,7 @@ 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', ], ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyshark-0.4.3/src/tox.ini new/pyshark-0.4.6/src/tox.ini --- old/pyshark-0.4.3/src/tox.ini 1970-01-01 01:00:00.000000000 +0100 +++ new/pyshark-0.4.6/src/tox.ini 2022-06-07 12:55:49.000000000 +0200 @@ -0,0 +1,11 @@ +[tox] +envlist = py{35,36,37,38,39,310} + +[testenv] +deps = + py + pytest + mock + lxml + packaging +commands = pytest .. -s \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyshark-0.4.3/tests/capture/test_inmem_capture.py new/pyshark-0.4.6/tests/capture/test_inmem_capture.py --- old/pyshark-0.4.3/tests/capture/test_inmem_capture.py 2021-02-06 08:44:25.000000000 +0100 +++ new/pyshark-0.4.6/tests/capture/test_inmem_capture.py 2022-06-07 12:55:49.000000000 +0200 @@ -17,7 +17,8 @@ def test_can_read_binary_packet(inmem_capture): - pkt = inmem_capture.feed_packet(arp_packet('f')) + pkt = inmem_capture.parse_packet(arp_packet('f')) + inmem_capture.close() assert pkt.eth.src == 'aa:bb:cc:dd:ee:ff' @@ -29,7 +30,7 @@ assert pkt.eth.src == 'aa:bb:cc:dd:ee:f' + str(i + 1) def test_fed_packets_are_added_to_the_list(inmem_capture): - inmem_capture.feed_packet(arp_packet()) + inmem_capture.feed_packets([arp_packet()]) assert len(inmem_capture) == 1 inmem_capture.feed_packets([arp_packet(), arp_packet()]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyshark-0.4.3/tests/capture/test_live_capture.py new/pyshark-0.4.6/tests/capture/test_live_capture.py --- old/pyshark-0.4.3/tests/capture/test_live_capture.py 2021-02-06 08:44:25.000000000 +0100 +++ new/pyshark-0.4.6/tests/capture/test_live_capture.py 2022-06-07 12:55:49.000000000 +0200 @@ -7,7 +7,7 @@ import pyshark [email protected]_fixture(params=[["wlan0"], ["wlan0mon", "wlan1mon"]]) [email protected](params=[["wlan0"], ["wlan0mon", "wlan1mon"]]) def interfaces(request): with mock.patch("pyshark.tshark.tshark.get_tshark_interfaces", return_value=request.param): yield request.param diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyshark-0.4.3/tests/test_cap_operations.py new/pyshark-0.4.6/tests/test_cap_operations.py --- old/pyshark-0.4.3/tests/test_cap_operations.py 2021-02-06 08:44:25.000000000 +0100 +++ new/pyshark-0.4.6/tests/test_cap_operations.py 2022-06-07 12:55:49.000000000 +0200 @@ -54,18 +54,17 @@ assert simple_summary_capture[0]._fields - def _iterate_capture_object(cap_obj, q): for packet in cap_obj: pass q.put(True) [email protected](reason="Don't know how to fix") def test_iterate_empty_psml_capture(simple_summary_capture): - # simple_summary_capture.display_filter = "frame.len == 1" + simple_summary_capture.display_filter = "frame.len == 1" q = Queue() - p = Process(target=_iterate_capture_object, args=(simple_summary_capture, q)) + p = Process(target=_iterate_capture_object, + args=(simple_summary_capture, q)) p.start() p.join(2) try: @@ -74,4 +73,4 @@ no_hang = False if p.is_alive(): p.terminate() - assert no_hang # False here + assert no_hang # False here diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyshark-0.4.3/tests/test_tshark.py new/pyshark-0.4.6/tests/test_tshark.py --- old/pyshark-0.4.3/tests/test_tshark.py 2021-02-06 08:44:25.000000000 +0100 +++ new/pyshark-0.4.6/tests/test_tshark.py 2022-06-07 12:55:49.000000000 +0200 @@ -1,4 +1,4 @@ -from distutils.version import LooseVersion +from packaging import version try: import mock @@ -28,16 +28,16 @@ b'1998-2014 Gerald Combs <[email protected]> and contributors.\n' ) actual = get_tshark_version() - expected = '1.12.1' + expected = version.parse('1.12.1') assert actual == expected def test_get_display_filter_flag(): - actual = get_tshark_display_filter_flag(LooseVersion('1.10.0')) + actual = get_tshark_display_filter_flag(version.parse('1.10.0')) expected = '-Y' assert actual == expected - actual = get_tshark_display_filter_flag(LooseVersion('1.6.0')) + actual = get_tshark_display_filter_flag(version.parse('1.6.0')) expected = '-R' assert actual == expected
