diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index c7a9e79..ccb31f3 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -9,7 +9,7 @@ jobs: strategy: max-parallel: 4 matrix: - python-version: [3.6, 3.7, 3.8] + python-version: [3.6, 3.7, 3.8, 3.9] steps: - uses: actions/checkout@v1 diff --git a/.travis.yml b/.travis.yml index 27859d6..58e8700 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,10 @@ language: python python: + - "3.9" - "3.8" - "3.7" - "3.6" - - "3.5" - "pypy3" install: diff --git a/HISTORY.rst b/HISTORY.rst index b45e31a..8493b16 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -2,7 +2,9 @@ tnefparse 1.4.0 (unreleased) ============================= -- drop Python2 support +- drop Python 2 support +- drop Python 3.5 support (jugmac00) +- add Python 3.9 support (jugmac00) tnefparse 1.3.1 (2020-09-30) ============================= diff --git a/README.rst b/README.rst index 0224bb7..0bc858b 100644 --- a/README.rst +++ b/README.rst @@ -14,7 +14,7 @@ tnefparse - TNEF decoding and attachment extraction :target: https://pypi.org/project/tnefparse/ This is a pure-python library for decoding Microsoft's Transport Neutral Encapsulation Format (TNEF), for Python -versions 3.5+ and PyPy3. The last version to support Python2 was 1.3.1. For more information on TNEF, see for example +versions 3.6+ and PyPy3. The last version to support Python2 was 1.3.1. For more information on TNEF, see for example `wikipedia `_. The full TNEF specification is also available as a `PDF download `_. diff --git a/scripts/make_props.py b/scripts/make_props.py index 96b6d31..e212133 100644 --- a/scripts/make_props.py +++ b/scripts/make_props.py @@ -1,5 +1,3 @@ -# -*- coding: future_fstrings -*- - """ Tool to regenerate tnefparse/properties.py @@ -13,7 +11,7 @@ properties = OrderedDict() -with open('data/properties.txt', 'r') as props: +with open('data/properties.txt') as props: for prop in props: prop = prop.strip() if not prop or prop.startswith('#'): diff --git a/setup.py b/setup.py index 857021c..3321c1b 100644 --- a/setup.py +++ b/setup.py @@ -19,10 +19,12 @@ 'Intended Audience :: End Users/Desktop', 'Programming Language :: Python', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: Implementation :: CPython', + 'Programming Language :: Python :: Implementation :: PyPy', 'Development Status :: 5 - Production/Stable', 'Environment :: Console', ], diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index ce9368c..5bac984 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -62,5 +62,5 @@ def test_dump(script_runner): ret = script_runner.run('tnefparse', '-d', 'tests/examples/two-files.tnef') assert ret.success dump = json.loads(ret.stdout) - assert sorted(list(dump.keys())) == [u'attachments', u'attributes', u'extended_attributes'] + assert sorted(list(dump.keys())) == ['attachments', 'attributes', 'extended_attributes'] assert len(dump['attachments']) == 2 diff --git a/tests/test_decoding.py b/tests/test_decoding.py index e3bfc23..d385aab 100644 --- a/tests/test_decoding.py +++ b/tests/test_decoding.py @@ -47,7 +47,7 @@ ("unicode-mapi-attr-name.tnef", 0x69ec, ['spaconsole2.cfg', 'image001.png', 'image002.png', 'image003.png'], 'htmlbody', []), ("unicode-mapi-attr.tnef", 0x408f, ['example.dat'], 'body', []), - ("umlaut.tnef", 0xa2e, ['TBZ PARIV GmbH.jpg', 'image003.jpg', u'UmlautAnhang-\xe4\xfc\xf6.txt'], 'rtfbody', []), + ("umlaut.tnef", 0xa2e, ['TBZ PARIV GmbH.jpg', 'image003.jpg', 'UmlautAnhang-\xe4\xfc\xf6.txt'], 'rtfbody', []), ("bad_checksum.tnef", 0x5784, ['image001.png'], 'body', []), ) diff --git a/tnefparse/codepage.py b/tnefparse/codepage.py index 1f94745..6584b97 100644 --- a/tnefparse/codepage.py +++ b/tnefparse/codepage.py @@ -8,7 +8,7 @@ FALLBACK = 'cp1252' -class Codepage(object): +class Codepage: def __init__(self, codepage): self.cp = codepage diff --git a/tnefparse/mapi.py b/tnefparse/mapi.py index ff37545..1d5adc3 100644 --- a/tnefparse/mapi.py +++ b/tnefparse/mapi.py @@ -1,7 +1,6 @@ "MAPI attribute definitions" import logging -import sys from decimal import Decimal from .util import ( @@ -19,9 +18,6 @@ ) from . import properties -if sys.hexversion < 0x03000000: - range = xrange # noqa: F821 - logger = logging.getLogger("mapi-decode") @@ -166,7 +162,7 @@ def parse_property(data, offset, attr_name, attr_type, codepage, is_multi): return attr_data, offset -class TNEFMAPI_Attribute(object): +class TNEFMAPI_Attribute: """represents a mapi attribute Property reference docs: diff --git a/tnefparse/tnef.py b/tnefparse/tnef.py index 67bda63..255c89f 100644 --- a/tnefparse/tnef.py +++ b/tnefparse/tnef.py @@ -13,7 +13,7 @@ logger = logging.getLogger(__package__) -class TNEFObject(object): +class TNEFObject: "a TNEF object that may contain a property and an attachment" PTYPE_CLASS = 0x1 # noqa: E221 PTYPE_TIME = 0x3 # noqa: E221 @@ -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("Checksum: %s != %s" % (calc_checksum, att_checksum)) + logger.warning(f"Checksum: {calc_checksum} != {att_checksum}") else: calc_checksum = att_checksum @@ -43,10 +43,10 @@ def name_str(self): return TNEF.codes.get(self.name) def __str__(self): - return "<%s '%s'>" % (self.__class__.__name__, self.name_str) + return f"<{self.__class__.__name__} '{self.name_str}'>" -class TNEFAttachment(object): +class TNEFAttachment: "a TNEF attachment" SZMAPI_UNSPECIFIED = 0x0000 # MAPI Unspecified @@ -143,7 +143,7 @@ def __str__(self): return "" % self.long_filename() -class TNEF(object): +class TNEF: "main decoder class - start by using this" TNEF_SIGNATURE = 0x223E9F78 @@ -336,7 +336,7 @@ def rtfbody(self): def __str__(self): atts = (", %i attachments" % len(self.attachments)) if self.attachments else '' - return "<%s:0x%2.2x%s>" % (self.__class__.__name__, self.key, atts) + return f"<{self.__class__.__name__}:0x{self.key:2.2x}{atts}>" def dump(self, force_strings=False): def get_data(a): @@ -390,7 +390,7 @@ def triples(data): return sender.rstrip(b'\x00'), etype, email.rstrip(b'\x00') -def to_zip(data, default_name=u'no-name', deflate=True): +def to_zip(data, default_name='no-name', deflate=True): "Convert attachments in TNEF data to zip format. Accepts and returns str type." # Parse the TNEF data tnef = TNEF(data) diff --git a/tnefparse/util.py b/tnefparse/util.py index cb843a1..24e4a65 100644 --- a/tnefparse/util.py +++ b/tnefparse/util.py @@ -2,14 +2,10 @@ """ import logging import struct -import sys import uuid import warnings from datetime import datetime, timedelta -if sys.hexversion < 0x03000000: - range = xrange # noqa: F821 - logger = logging.getLogger(__package__) @@ -64,26 +60,11 @@ def typtime(byte_arr, offset=0): return datetime(*parts) -def bytes_to_int_py3(byte_arr): - "transform multi-byte values into integers, python3 version" +def bytes_to_int(byte_arr): + "transform multi-byte values into integers" return int.from_bytes(byte_arr, byteorder="little", signed=False) -def bytes_to_int_py2(byte_arr): - "transform multi-byte values into integers, python2 version" - n = num = 0 - for b in byte_arr: - num += ord(b) << n - n += 8 - return num - - -if sys.hexversion > 0x03000000: - bytes_to_int = bytes_to_int_py3 -else: - bytes_to_int = bytes_to_int_py2 - - def checksum(data): return sum(bytearray(data)) & 0xFFFF diff --git a/tox.ini b/tox.ini index 1c30b39..b040bc1 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,5 @@ [tox] -# Python 3.5 has TLS issues on OSX at least -envlist = py36, py37, py38, coverage, check-manifest +envlist = py36, py37, py38, py39, coverage, check-manifest, flake8 [testenv] # install the `optional` requirements