From 72b5fce58c7d5baeb4be946ee454f4c2f588cc35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= <6774676+eumiro@users.noreply.github.com> Date: Wed, 25 Nov 2020 18:12:00 +0100 Subject: [PATCH 1/5] Use % formatting in log messages --- tnefparse/cmdline.py | 2 +- tnefparse/mapi.py | 2 +- tnefparse/tnef.py | 8 ++++---- tnefparse/util.py | 38 ++++++++++++++++++-------------------- 4 files changed, 24 insertions(+), 26 deletions(-) diff --git a/tnefparse/cmdline.py b/tnefparse/cmdline.py index dfa9f45..908af9a 100644 --- a/tnefparse/cmdline.py +++ b/tnefparse/cmdline.py @@ -83,7 +83,7 @@ def tnefparse(): try: print(" " + properties.CODE_TO_NAME[p.name]) except KeyError: - logger.warning("Unknown MAPI Property: %s" % hex(p.name)) + logger.warning("Unknown MAPI Property: 0x%x", p.name) print("") elif args.dump: diff --git a/tnefparse/mapi.py b/tnefparse/mapi.py index 1d5adc3..cdaf12e 100644 --- a/tnefparse/mapi.py +++ b/tnefparse/mapi.py @@ -56,7 +56,7 @@ def decode_mapi(data, codepage='cp1252', starting_offset=None): try: for i in range(num_properties): if offset >= dataLen: - logger.warn("Skipping property '%i'" % i) + logger.warn("Skipping property %r", i) continue attr_type = uint16(data, offset) diff --git a/tnefparse/tnef.py b/tnefparse/tnef.py index 255c89f..7edb7ae 100644 --- a/tnefparse/tnef.py +++ b/tnefparse/tnef.py @@ -31,7 +31,7 @@ def __init__(self, data, do_checksum=False): if do_checksum: calc_checksum = checksum(self.data) if calc_checksum != att_checksum: - logger.warning(f"Checksum: {calc_checksum} != {att_checksum}") + logger.warning("Checksum: %s != %s", calc_checksum, att_checksum) else: calc_checksum = att_checksum @@ -137,7 +137,7 @@ def add_attr(self, attribute): pass # this is a WMF file of some kind else: - logger.debug("Unknown attribute name: %s" % attribute) + logger.debug("Unknown attribute name: %s", attribute) def __str__(self): return "" % self.long_filename() @@ -315,9 +315,9 @@ def __init__(self, data, do_checksum=True): obj.data = typtime(obj.data) self.msgprops.append(obj) except ValueError: - logger.debug("TNEF Object not a valid date: %s" % obj) + logger.debug("TNEF Object not a valid date: %s", obj) else: - logger.debug("Unhandled TNEF Object: %s" % obj) + logger.debug("Unhandled TNEF Object: %s", obj) def has_body(self): return True if (self.body or self.htmlbody or self._rtfbody) else False diff --git a/tnefparse/util.py b/tnefparse/util.py index 24e4a65..165c8b3 100644 --- a/tnefparse/util.py +++ b/tnefparse/util.py @@ -77,25 +77,23 @@ def raw_mapi(dataLen, data): while loop <= dataLen: if (loop + 16) < dataLen: logger.debug( - "%2.2x%2.2x %2.2x%2.2x %2.2x%2.2x %2.2x%2.2x %2.2x%2.2x %2.2x%2.2x %2.2x%2.2x %2.2x%2.2x" - % ( - ord(data[loop]), - ord(data[loop + 1]), - ord(data[loop + 2]), - ord(data[loop + 3]), - ord(data[loop + 4]), - ord(data[loop + 5]), - ord(data[loop + 6]), - ord(data[loop + 7]), - ord(data[loop + 8]), - ord(data[loop + 9]), - ord(data[loop + 10]), - ord(data[loop + 11]), - ord(data[loop + 12]), - ord(data[loop + 13]), - ord(data[loop + 14]), - ord(data[loop + 15]), - ) + "%2.2x%2.2x %2.2x%2.2x %2.2x%2.2x %2.2x%2.2x %2.2x%2.2x %2.2x%2.2x %2.2x%2.2x %2.2x%2.2x", + ord(data[loop]), + ord(data[loop + 1]), + ord(data[loop + 2]), + ord(data[loop + 3]), + ord(data[loop + 4]), + ord(data[loop + 5]), + ord(data[loop + 6]), + ord(data[loop + 7]), + ord(data[loop + 8]), + ord(data[loop + 9]), + ord(data[loop + 10]), + ord(data[loop + 11]), + ord(data[loop + 12]), + ord(data[loop + 13]), + ord(data[loop + 14]), + ord(data[loop + 15]), ) loop += 16 loop -= 16 @@ -106,4 +104,4 @@ def raw_mapi(dataLen, data): if i != 0 and subr == 0: strList.append(' ') strList.append('%2.2x' % ord(data[loop + i])) - logger.debug('%s' % ''.join(strList)) + logger.debug(''.join(strList)) From c6abdf0efb6bfd059320cd4a25c09ce26898c253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= <6774676+eumiro@users.noreply.github.com> Date: Wed, 25 Nov 2020 18:13:35 +0100 Subject: [PATCH 2/5] Log exception's stack properly --- tnefparse/mapi.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tnefparse/mapi.py b/tnefparse/mapi.py index 1d5adc3..14b238e 100644 --- a/tnefparse/mapi.py +++ b/tnefparse/mapi.py @@ -106,12 +106,9 @@ def decode_mapi(data, codepage='cp1252', starting_offset=None): if (num_mv_properties or 1) % 2 and attr_type in (SZMAPI_SHORT, SZMAPI_BOOLEAN): offset += 2 - except Exception as e: - import traceback - - stack = traceback.format_exc() - logger.error('decode_mapi Exception %s' % e) - logger.debug(stack) + except Exception as exc: + logger.error('decode_mapi exception: %s', exc) + logger.debug('exception details:', exc_info=True) if starting_offset is not None: return (offset, attrs) From b502a382c653531d100b1ee278ae455e129a2a1b Mon Sep 17 00:00:00 2001 From: Petri Savolainen Date: Wed, 25 Nov 2020 23:20:02 +0200 Subject: [PATCH 3/5] parseFile is deprecated post-1.3 --- tnefparse/__init__.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tnefparse/__init__.py b/tnefparse/__init__.py index cd12fad..8505d5b 100644 --- a/tnefparse/__init__.py +++ b/tnefparse/__init__.py @@ -5,8 +5,3 @@ logging.getLogger(__package__).addHandler(logging.NullHandler()) - -def parseFile(fileobj): - "a convenience function that returns a TNEF object" - warnings.warn("parseFile will be deprecated after 1.3", DeprecationWarning) - return TNEF(fileobj.read()) From f5b7b17d860c8de3f7e62ec3afd539399c9fd6d1 Mon Sep 17 00:00:00 2001 From: Petri Savolainen Date: Wed, 25 Nov 2020 23:25:11 +0200 Subject: [PATCH 4/5] remove unused import --- tnefparse/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tnefparse/__init__.py b/tnefparse/__init__.py index 8505d5b..289b935 100644 --- a/tnefparse/__init__.py +++ b/tnefparse/__init__.py @@ -1,7 +1,5 @@ import logging -import warnings from .tnef import TNEF, TNEFAttachment, TNEFObject # noqa: F401 logging.getLogger(__package__).addHandler(logging.NullHandler()) - From fc3e0bc66a243151045bcc76db81ea987ba28fde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Gmach?= Date: Thu, 26 Nov 2020 08:32:19 +0100 Subject: [PATCH 5/5] Introduce type annotations You can run the type checker with `tox -e mypy`. This is just a start. - A lot of type annotations are still missing. - The type checker is not yet using the strict option. - Type annotations are not yet enforced in CI. modified: tnefparse/cmdline.py modified: tnefparse/codepage.py modified: tnefparse/tnef.py modified: tox.ini --- HISTORY.rst | 1 + tnefparse/cmdline.py | 4 ++-- tnefparse/codepage.py | 10 ++++++---- tnefparse/tnef.py | 7 ++++--- tox.ini | 7 +++++++ 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 8493b16..3a0aa20 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -5,6 +5,7 @@ tnefparse 1.4.0 (unreleased) - drop Python 2 support - drop Python 3.5 support (jugmac00) - add Python 3.9 support (jugmac00) +- introduce using type annotations (jugmac00) tnefparse 1.3.1 (2020-09-30) ============================= diff --git a/tnefparse/cmdline.py b/tnefparse/cmdline.py index 845b074..a732d7b 100644 --- a/tnefparse/cmdline.py +++ b/tnefparse/cmdline.py @@ -47,7 +47,7 @@ help="extract a json dump of the tnef contents") -def tnefparse(): +def tnefparse() -> None: "command-line script" if len(sys.argv) == 1: @@ -97,7 +97,7 @@ def tnefparse(): sys.stderr.write("Successfully wrote %i files\n" % len(t.attachments)) sys.exit() - def print_body(attr, description): + def print_body(attr: str, description: str) -> None: body = getattr(t, attr) if body is None: sys.exit("No %s found" % description) diff --git a/tnefparse/codepage.py b/tnefparse/codepage.py index 6584b97..02107ec 100644 --- a/tnefparse/codepage.py +++ b/tnefparse/codepage.py @@ -1,3 +1,5 @@ +from typing import Optional, Union + # https://docs.microsoft.com/en-us/windows/desktop/intl/code-page-identifiers CODEPAGE_MAP = { 20127: 'ascii', @@ -9,10 +11,10 @@ class Codepage: - def __init__(self, codepage): + def __init__(self, codepage: int) -> None: self.cp = codepage - def best_guess(self): + def best_guess(self) -> Optional[str]: if CODEPAGE_MAP.get(self.cp): return CODEPAGE_MAP.get(self.cp) elif self.cp <= 1258: # max cpXXXX page in python @@ -20,14 +22,14 @@ def best_guess(self): else: return None - def codepage(self): + def codepage(self) -> str: bg = self.best_guess() if bg: return bg else: return 'cp%d' % self.cp - def decode(self, byte_str): + def decode(self, byte_str: Union[str, bytes]) -> str: if isinstance(byte_str, bytes): return byte_str.decode(self.best_guess() or FALLBACK) else: diff --git a/tnefparse/tnef.py b/tnefparse/tnef.py index 7edb7ae..bfc8f8b 100644 --- a/tnefparse/tnef.py +++ b/tnefparse/tnef.py @@ -97,13 +97,13 @@ def __init__(self, codepage): self.data = b'' @property - def name(self): + def name(self) -> str: if isinstance(self._name, bytes): return self._name.decode().strip('\x00') else: return self._name.strip('\x00') - def long_filename(self): + def long_filename(self) -> str: atname = Attribute.MAPI_ATTACH_LONG_FILENAME name = [a.data for a in self.mapi_attrs if a.name == atname] if name: @@ -326,7 +326,8 @@ def has_body(self): def rtfbody(self): if self._rtfbody: try: - from compressed_rtf import decompress + # compressed_rtf is not typed yet + from compressed_rtf import decompress # type: ignore return decompress(self._rtfbody + b'\x00') except ImportError: logger.warning("Returning compressed RTF. Install compressed_rtf to decompress") diff --git a/tox.ini b/tox.ini index b040bc1..788dc17 100644 --- a/tox.ini +++ b/tox.ini @@ -43,6 +43,13 @@ deps = commands = check-manifest +[testenv:mypy] +deps = + mypy +commands = + # do not lint tests yet + mypy tnefparse {posargs} + [flake8] # The GitHub editor is 127 chars wide. max-line-length = 127