From 84c2dfb4199ca7f227a489422cb2bb3edcdd9c34 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <hugovk@users.noreply.github.com> Date: Tue, 23 Nov 2021 17:14:37 +0200 Subject: [PATCH 01/12] Add support for Python 3.8-3.10 --- .github/workflows/test.yaml | 2 +- setup.py | 3 +++ tox.ini | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 19741b8..dded674 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.6, 3.7, 3.8, 3.9] + python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"] steps: - uses: actions/checkout@v2 diff --git a/setup.py b/setup.py index aefc52c..834811a 100755 --- a/setup.py +++ b/setup.py @@ -53,6 +53,9 @@ '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 :: 3.10', 'Topic :: Software Development :: Libraries :: Python Modules' ], test_suite = 'multipart.tests.suite', diff --git a/tox.ini b/tox.ini index 1aa01f1..fc58f96 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27,py31,py32,py33 +envlist = py27,py31,py32,py33,py36,py37,py38,py39,py310 [testenv] deps= From 44c111ec4daf8f83951ae57a733df987cf9224a4 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <hugovk@users.noreply.github.com> Date: Tue, 23 Nov 2021 17:15:52 +0200 Subject: [PATCH 02/12] Remove redundant Travis CI config, GHA is used for CI --- .travis.yml | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 45068e0..0000000 --- a/.travis.yml +++ /dev/null @@ -1,20 +0,0 @@ -sudo: false -language: python - -python: - - "2.7" - - "3.4" - - "3.5" - - "3.6" - - "3.7" - - "pypy" - -install: - - pip install -r requirements.txt - -script: - - py.test - -branches: - only: - - master From 85d84d7431b0c21e4265a626f07af09f3d56798a Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <hugovk@users.noreply.github.com> Date: Tue, 23 Nov 2021 17:17:40 +0200 Subject: [PATCH 03/12] Update build badge for GHA --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 898fc9d..508b14e 100644 --- a/README.rst +++ b/README.rst @@ -2,8 +2,8 @@ Python-Multipart ================== -.. image:: https://secure.travis-ci.org/andrew-d/python-multipart.png?branch=master - :target: http://travis-ci.org/andrew-d/python-multipart +.. image:: https://github.com/andrew-d/python-multipart/actions/workflows/test.yaml/badge.svg + :target: https://github.com/andrew-d/python-multipart/actions python-multipart is an Apache2 licensed streaming multipart parser for Python. From 3a4a0295f300d22787813ab4cdf7654e1a21ee82 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <hugovk@users.noreply.github.com> Date: Tue, 23 Nov 2021 17:20:25 +0200 Subject: [PATCH 04/12] Drop the dot https://twitter.com/pytestdotorg/status/753767547866972160 --- README.rst | 2 +- multipart/tests/compat.py | 2 +- tasks.py | 2 +- tox.ini | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 508b14e..aceb6e2 100644 --- a/README.rst +++ b/README.rst @@ -25,4 +25,4 @@ If you want to test: .. code-block:: bash $ pip install -r requirements.txt - $ py.test + $ pytest diff --git a/multipart/tests/compat.py b/multipart/tests/compat.py index 7a3479e..c7193e5 100644 --- a/multipart/tests/compat.py +++ b/multipart/tests/compat.py @@ -58,7 +58,7 @@ def xfail(*args, **kwargs): return lambda x: x -# We don't use the py.test parametrizing function, since it seems to break +# We don't use the pytest parametrizing function, since it seems to break # with unittest.TestCase subclasses. def parametrize(field_names, field_values): # If we're not given a list of field names, we make it. diff --git a/tasks.py b/tasks.py index 727b950..77934d1 100644 --- a/tasks.py +++ b/tasks.py @@ -18,7 +18,7 @@ class g(object): @task def test(all=False): test_cmd = [ - 'py.test', # Test command + 'pytest', # Test command '--cov-report term-missing', # Print only uncovered lines to stdout '--cov-config .coveragerc', # Use this file for configuration '--cov multipart', # Test only this module diff --git a/tox.ini b/tox.ini index fc58f96..00f9bff 100644 --- a/tox.ini +++ b/tox.ini @@ -9,7 +9,7 @@ deps= mock PyYAML commands= - py.test --cov-report term-missing --cov-config .coveragerc --cov multipart --timeout=30 multipart/tests + pytest --cov-report term-missing --cov-config .coveragerc --cov multipart --timeout=30 multipart/tests [testenv:py31] basepython=/usr/local/Cellar/python31/3.1.5/bin/python3.1 From b2117c113704b14253301a06d7c96f5a7a77fd38 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <hugovk@users.noreply.github.com> Date: Tue, 23 Nov 2021 17:25:08 +0200 Subject: [PATCH 05/12] Bump attrs, pluggy and pytest pins to support Python 3.10 --- docs_requirements.txt | 2 +- requirements.txt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs_requirements.txt b/docs_requirements.txt index 0be06a5..55b9125 100644 --- a/docs_requirements.txt +++ b/docs_requirements.txt @@ -10,7 +10,7 @@ invoke==0.2.0 mock==1.0.1 pexpect-u==2.5.1 py==1.10.0 -pytest==2.3.4 +pytest==6.2.5 pytest-capturelog==0.7 pytest-cov==1.6 pytest-timeout==0.3 diff --git a/requirements.txt b/requirements.txt index 8c7c701..37dc501 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,11 @@ atomicwrites==1.2.1 -attrs==18.2.0 +attrs==19.2.0 coverage==4.5.1 mock==2.0.0 more-itertools==4.3.0 pbr==4.3.0 -pluggy==0.7.1 +pluggy==1.0.0 py==1.10.0 -pytest==3.8.2 +pytest==6.2.5 PyYAML==5.1 six==1.11.0 From 763ce7f2145533a34704cc53ee00a0f12a68f9f9 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <hugovk@users.noreply.github.com> Date: Tue, 23 Nov 2021 17:29:52 +0200 Subject: [PATCH 06/12] Drop support or EOL Python 2.7-3.5 --- setup.py | 15 +++------------ tox.ini | 5 +---- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/setup.py b/setup.py index 834811a..6f02a8d 100755 --- a/setup.py +++ b/setup.py @@ -3,15 +3,11 @@ import os import re -import sys from setuptools import setup version_file = os.path.join('multipart', '_version.py') with open(version_file, 'rb') as f: - version_data = f.read().strip() - -if sys.version_info[0] >= 3: - version_data = version_data.decode('ascii') + version_data = f.read().strip().decode('ascii') version_re = re.compile(r'((?:\d+)\.(?:\d+)\.(?:\d+))') version = version_re.search(version_data).group(0) @@ -22,9 +18,6 @@ 'PyYAML' ] -if sys.version_info[0:2] < (3, 3): - tests_require.append('mock') - setup(name='python-multipart', version=version, description='A streaming multipart parser for Python', @@ -47,10 +40,8 @@ 'Intended Audience :: Developers', 'License :: OSI Approved :: Apache Software License', 'Operating System :: OS Independent', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3 :: Only', + 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', diff --git a/tox.ini b/tox.ini index 00f9bff..c62cdb1 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27,py31,py32,py33,py36,py37,py38,py39,py310 +envlist = py36,py37,py38,py39,py310 [testenv] deps= @@ -10,6 +10,3 @@ deps= PyYAML commands= pytest --cov-report term-missing --cov-config .coveragerc --cov multipart --timeout=30 multipart/tests - -[testenv:py31] -basepython=/usr/local/Cellar/python31/3.1.5/bin/python3.1 From 2301ca92066163a2576bc8dce07a41b908b0f8ba Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <hugovk@users.noreply.github.com> Date: Tue, 23 Nov 2021 17:33:28 +0200 Subject: [PATCH 07/12] Upgrade Python syntax with pyupgrade --py36-plus --- docs/source/conf.py | 17 ++++----- multipart/__init__.py | 1 - multipart/decoders.py | 8 ++-- multipart/multipart.py | 63 ++++++++++++++----------------- multipart/tests/compat.py | 2 +- multipart/tests/test_multipart.py | 14 +++---- setup.py | 1 - tasks.py | 10 ++--- 8 files changed, 51 insertions(+), 65 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index a260547..8851bc7 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # python-multipart documentation build configuration file, created by # sphinx-quickstart on Fri Apr 5 20:24:27 2013. @@ -42,8 +41,8 @@ master_doc = 'index' # General information about the project. -project = u'python-multipart' -copyright = u'2013, Andrew Dunham' +project = 'python-multipart' +copyright = '2013, Andrew Dunham' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -192,8 +191,8 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'python-multipart.tex', u'python-multipart Documentation', - u'Andrew Dunham', 'manual'), + ('index', 'python-multipart.tex', 'python-multipart Documentation', + 'Andrew Dunham', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -222,8 +221,8 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'python-multipart', u'python-multipart Documentation', - [u'Andrew Dunham'], 1) + ('index', 'python-multipart', 'python-multipart Documentation', + ['Andrew Dunham'], 1) ] # If true, show URL addresses after external links. @@ -236,8 +235,8 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'python-multipart', u'python-multipart Documentation', - u'Andrew Dunham', 'python-multipart', 'One line description of project.', + ('index', 'python-multipart', 'python-multipart Documentation', + 'Andrew Dunham', 'python-multipart', 'One line description of project.', 'Miscellaneous'), ] diff --git a/multipart/__init__.py b/multipart/__init__.py index cbe9211..1f04bca 100644 --- a/multipart/__init__.py +++ b/multipart/__init__.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import import sys # This is the canonical package information. diff --git a/multipart/decoders.py b/multipart/decoders.py index 06cb23f..6dac55e 100644 --- a/multipart/decoders.py +++ b/multipart/decoders.py @@ -4,7 +4,7 @@ from .exceptions import Base64Error, DecodeError -class Base64Decoder(object): +class Base64Decoder: """This object provides an interface to decode a stream of Base64 data. It is instantiated with an "underlying object", and whenever a write() operation is performed, it will decode the incoming data as Base64, and @@ -99,10 +99,10 @@ def finalize(self): self.underlying.finalize() def __repr__(self): - return "%s(underlying=%r)" % (self.__class__.__name__, self.underlying) + return f"{self.__class__.__name__}(underlying={self.underlying!r})" -class QuotedPrintableDecoder(object): +class QuotedPrintableDecoder: """This object provides an interface to decode a stream of quoted-printable data. It is instantiated with an "underlying object", in the same manner as the :class:`multipart.decoders.Base64Decoder` class. This class behaves @@ -168,4 +168,4 @@ def finalize(self): self.underlying.finalize() def __repr__(self): - return "%s(underlying=%r)" % (self.__class__.__name__, self.underlying) + return f"{self.__class__.__name__}(underlying={self.underlying!r})" diff --git a/multipart/multipart.py b/multipart/multipart.py index 11c062e..6fac9d1 100644 --- a/multipart/multipart.py +++ b/multipart/multipart.py @@ -1,5 +1,3 @@ -from __future__ import with_statement, absolute_import, print_function - from six import ( binary_type, text_type, @@ -74,14 +72,9 @@ # str on Py2, and bytes on Py3. Same with getting the ordinal value of a byte, # and joining a list of bytes together. # These functions abstract that. -if PY3: # pragma: no cover - lower_char = lambda c: c | 0x20 - ord_char = lambda c: c - join_bytes = lambda b: bytes(list(b)) -else: # pragma: no cover - lower_char = lambda c: c.lower() - ord_char = lambda c: ord(c) - join_bytes = lambda b: b''.join(list(b)) +lower_char = lambda c: c | 0x20 +ord_char = lambda c: c +join_bytes = lambda b: bytes(list(b)) # These are regexes for parsing header values. SPECIAL_CHARS = re.escape(b'()<>@,;:\\"/[]?={} \t') @@ -104,7 +97,7 @@ def parse_options_header(value): # If we are passed a string, we assume that it conforms to WSGI and does # not contain any code point that's not in latin-1. - if isinstance(value, text_type): # pragma: no cover + if isinstance(value, str): # pragma: no cover value = value.encode('latin-1') # If we have no options, return the string as-is. @@ -135,7 +128,7 @@ def parse_options_header(value): return ctype, options -class Field(object): +class Field: """A Field object represents a (parsed) form field. It represents a single field with a corresponding name and value. @@ -252,14 +245,14 @@ def __repr__(self): else: v = repr(self.value) - return "%s(field_name=%r, value=%s)" % ( + return "{}(field_name={!r}, value={})".format( self.__class__.__name__, self.field_name, v ) -class File(object): +class File: """This class represents an uploaded file. It handles writing file data to either an in-memory file or a temporary file on-disk, if the optional threshold is passed. @@ -442,7 +435,7 @@ def _get_disk_file(self): try: self.logger.info("Opening file: %r", path) tmp_file = open(path, 'w+b') - except (IOError, OSError) as e: + except OSError as e: tmp_file = None self.logger.exception("Error opening temporary file") @@ -454,13 +447,13 @@ def _get_disk_file(self): options = {} if keep_extensions: ext = self._ext - if isinstance(ext, binary_type): + if isinstance(ext, bytes): ext = ext.decode(sys.getfilesystemencoding()) options['suffix'] = ext if file_dir is not None: d = file_dir - if isinstance(d, binary_type): + if isinstance(d, bytes): d = d.decode(sys.getfilesystemencoding()) options['dir'] = d @@ -471,14 +464,14 @@ def _get_disk_file(self): options) try: tmp_file = tempfile.NamedTemporaryFile(**options) - except (IOError, OSError): + except OSError: self.logger.exception("Error creating named temporary file") raise FileError("Error creating named temporary file") fname = tmp_file.name # Encode filename as bytes. - if isinstance(fname, text_type): + if isinstance(fname, str): fname = fname.encode(sys.getfilesystemencoding()) self._actual_file_name = fname @@ -543,14 +536,14 @@ def close(self): self._fileobj.close() def __repr__(self): - return "%s(file_name=%r, field_name=%r)" % ( + return "{}(file_name={!r}, field_name={!r})".format( self.__class__.__name__, self.file_name, self.field_name ) -class BaseParser(object): +class BaseParser: """This class is the base class for all parsers. It contains the logic for calling and adding callbacks. @@ -657,7 +650,7 @@ class OctetStreamParser(BaseParser): i.e. unbounded. """ def __init__(self, callbacks={}, max_size=float('inf')): - super(OctetStreamParser, self).__init__() + super().__init__() self.callbacks = callbacks self._started = False @@ -750,7 +743,7 @@ class QuerystringParser(BaseParser): """ def __init__(self, callbacks={}, strict_parsing=False, max_size=float('inf')): - super(QuerystringParser, self).__init__() + super().__init__() self.state = STATE_BEFORE_FIELD self._found_sep = False @@ -949,7 +942,7 @@ def finalize(self): self.callback('end') def __repr__(self): - return "%s(strict_parsing=%r, max_size=%r)" % ( + return "{}(strict_parsing={!r}, max_size={!r})".format( self.__class__.__name__, self.strict_parsing, self.max_size ) @@ -1012,7 +1005,7 @@ class MultipartParser(BaseParser): def __init__(self, boundary, callbacks={}, max_size=float('inf')): # Initialize parser state. - super(MultipartParser, self).__init__() + super().__init__() self.state = STATE_START self.index = self.flags = 0 @@ -1037,7 +1030,7 @@ def __init__(self, boundary, callbacks={}, max_size=float('inf')): # self.skip = tuple(skip) # Save our boundary. - if isinstance(boundary, text_type): # pragma: no cover + if isinstance(boundary, str): # pragma: no cover boundary = boundary.encode('latin-1') self.boundary = b'\r\n--' + boundary @@ -1283,7 +1276,7 @@ def data_callback(name, remaining=False): # a CR at the beginning of a header, so our next character # should be a LF, or it's an error. if c != LF: - msg = "Did not find LF at end of headers (found %r)" % (c,) + msg = f"Did not find LF at end of headers (found {c!r})" self.logger.warning(msg) e = MultipartParseError(msg) e.offset = i @@ -1483,10 +1476,10 @@ def finalize(self): pass def __repr__(self): - return "%s(boundary=%r)" % (self.__class__.__name__, self.boundary) + return f"{self.__class__.__name__}(boundary={self.boundary!r})" -class FormParser(object): +class FormParser: """This class is the all-in-one form parser. Given all the information necessary to parse a form, it will instantiate the correct parser, create the proper :class:`Field` and :class:`File` classes to store the data that @@ -1579,7 +1572,7 @@ def __init__(self, content_type, on_field, on_file, on_end=None, # Depending on the Content-Type, we instantiate the correct parser. if content_type == 'application/octet-stream': # Work around the lack of 'nonlocal' in Py2 - class vars(object): + class vars: f = None def on_start(): @@ -1614,7 +1607,7 @@ def on_end(): name_buffer = [] - class vars(object): + class vars: f = None def on_field_start(): @@ -1671,7 +1664,7 @@ def on_end(): headers = {} # No 'nonlocal' on Python 2 :-( - class vars(object): + class vars: f = None writer = None is_file = False @@ -1745,7 +1738,7 @@ def on_headers_finished(): "%r", transfer_encoding) if self.config['UPLOAD_ERROR_ON_BAD_CTE']: raise FormParserError( - 'Unknown Content-Transfer-Encoding "{0}"'.format( + 'Unknown Content-Transfer-Encoding "{}"'.format( transfer_encoding ) ) @@ -1777,7 +1770,7 @@ def on_end(): else: self.logger.warning("Unknown Content-Type: %r", content_type) - raise FormParserError("Unknown Content-Type: {0}".format( + raise FormParserError("Unknown Content-Type: {}".format( content_type )) @@ -1804,7 +1797,7 @@ def close(self): self.parser.close() def __repr__(self): - return "%s(content_type=%r, parser=%r)" % ( + return "{}(content_type={!r}, parser={!r})".format( self.__class__.__name__, self.content_type, self.parser, diff --git a/multipart/tests/compat.py b/multipart/tests/compat.py index c7193e5..0fb84c3 100644 --- a/multipart/tests/compat.py +++ b/multipart/tests/compat.py @@ -20,7 +20,7 @@ def ensure_in_path(path): def _samefile(x, y): try: return os.path.samefile(x, y) - except (IOError, OSError): + except OSError: return False except AttributeError: # Probably on Windows. diff --git a/multipart/tests/test_multipart.py b/multipart/tests/test_multipart.py index 0e38c9c..7e49e16 100644 --- a/multipart/tests/test_multipart.py +++ b/multipart/tests/test_multipart.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import os import sys import glob @@ -19,7 +17,7 @@ try: from unittest.mock import MagicMock, Mock, patch except ImportError: - from mock import MagicMock, Mock, patch + from unittest.mock import MagicMock, Mock, patch from ..multipart import * @@ -29,7 +27,7 @@ def force_bytes(val): - if isinstance(val, text_type): + if isinstance(val, str): val = val.encode(sys.getfilesystemencoding()) return val @@ -427,7 +425,7 @@ def test_strict_parsing_pass(self): self.reset() self.p.strict_parsing = True - print("%r / %r" % (first, last)) + print(f"{first!r} / {last!r}") self.p.write(first) self.p.write(last) @@ -453,7 +451,7 @@ def test_strict_parsing_fail_double_sep(self): def test_double_sep(self): data = b'foo=bar&&another=asdf' for first, last in split_all(data): - print(" %r / %r " % (first, last)) + print(f" {first!r} / {last!r} ") self.reset() cnt = 0 @@ -699,7 +697,7 @@ def test_not_aligned(self): http_tests_dir = os.path.join(curr_dir, 'test_data', 'http') # Read in all test cases and load them. -NON_PARAMETRIZED_TESTS = set(['single_field_blocks']) +NON_PARAMETRIZED_TESTS = {'single_field_blocks'} http_tests = [] for f in os.listdir(http_tests_dir): # Only load the HTTP test cases. @@ -803,7 +801,7 @@ def assert_field(self, name, value): def test_http(self, param): # Firstly, create our parser with the given boundary. boundary = param['result']['boundary'] - if isinstance(boundary, text_type): + if isinstance(boundary, str): boundary = boundary.encode('latin-1') self.make(boundary) diff --git a/setup.py b/setup.py index 6f02a8d..dd7542e 100755 --- a/setup.py +++ b/setup.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -from __future__ import print_function import os import re diff --git a/tasks.py b/tasks.py index 77934d1..b099df3 100644 --- a/tasks.py +++ b/tasks.py @@ -1,5 +1,3 @@ -from __future__ import print_function - import os import re import sys @@ -11,7 +9,7 @@ version_regex = re.compile(r'((?:\d+)\.(?:\d+)\.(?:\d+))') # Get around Python 2.X's lack of 'nonlocal' keyword -class g(object): +class g: test_success = False @@ -46,7 +44,7 @@ def bump(type): m = version_regex.search(file_data) if m is None: - print("Could not find version in '{0}'!".format(version_file), file=sys.stderr) + print(f"Could not find version in '{version_file}'!", file=sys.stderr) return version = m.group(0) @@ -63,7 +61,7 @@ def bump(type): elif type == 'major': ver_nums[0] += 1 else: - print("Invalid version type: '%s'" % (type,), file=sys.stderr) + print(f"Invalid version type: '{type}'", file=sys.stderr) return # Construct new data and write to file. @@ -74,7 +72,7 @@ def bump(type): f.write(new_data) # Print information. - print("Bumped version from: %s --> %s" % (version, new_ver)) + print(f"Bumped version from: {version} --> {new_ver}") @task(pre=['test']) From 051e79d89e7fc12541eb59e2c6c88ae2a17208a3 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <hugovk@users.noreply.github.com> Date: Tue, 23 Nov 2021 17:37:58 +0200 Subject: [PATCH 08/12] Drop support for EOL Python 2.7 --- docs/source/conf.py | 1 - multipart/decoders.py | 4 ++-- multipart/exceptions.py | 12 ------------ 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 8851bc7..8f1e0ae 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,4 +1,3 @@ -# # python-multipart documentation build configuration file, created by # sphinx-quickstart on Fri Apr 5 20:24:27 2013. # diff --git a/multipart/decoders.py b/multipart/decoders.py index 6dac55e..0d7ab32 100644 --- a/multipart/decoders.py +++ b/multipart/decoders.py @@ -1,7 +1,7 @@ import base64 import binascii -from .exceptions import Base64Error, DecodeError +from .exceptions import DecodeError class Base64Decoder: @@ -58,7 +58,7 @@ def write(self, data): if len(val) > 0: try: decoded = base64.b64decode(val) - except Base64Error: + except binascii.Error: raise DecodeError('There was an error raised while decoding ' 'base64-encoded data.') diff --git a/multipart/exceptions.py b/multipart/exceptions.py index 98c5867..3264b82 100644 --- a/multipart/exceptions.py +++ b/multipart/exceptions.py @@ -1,8 +1,3 @@ -import binascii - -from six import PY3 - - class FormParserError(ValueError): """Base error class for our form parser.""" pass @@ -49,10 +44,3 @@ class FileError(FormParserError, IOError, OSError): class FileError(FormParserError, OSError): """Exception class for problems with the File class.""" pass - -# We check which version of Python we're on to figure out what error we need -# to catch for invalid Base64. -if PY3: # pragma: no cover - Base64Error = binascii.Error -else: # pragma: no cover - Base64Error = TypeError From c54ad6006bacc77623864ec8e5c96bfd32230e01 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <hugovk@users.noreply.github.com> Date: Tue, 23 Nov 2021 17:40:08 +0200 Subject: [PATCH 09/12] Remove redundant six --- .coveragerc | 4 ---- multipart/multipart.py | 6 ------ multipart/tests/test_multipart.py | 1 - requirements.txt | 1 - setup.py | 3 --- 5 files changed, 15 deletions(-) diff --git a/.coveragerc b/.coveragerc index 685c6e2..0f1b93d 100644 --- a/.coveragerc +++ b/.coveragerc @@ -5,7 +5,6 @@ branch = False # branch = True omit = - *six.py multipart/tests/* [report] @@ -33,6 +32,3 @@ exclude_lines = # Ignore import exceptions except ImportError: - # Ignore python compatibility. - if PY3 - diff --git a/multipart/multipart.py b/multipart/multipart.py index 6fac9d1..e66c538 100644 --- a/multipart/multipart.py +++ b/multipart/multipart.py @@ -1,9 +1,3 @@ -from six import ( - binary_type, - text_type, - PY3, -) - from .decoders import * from .exceptions import * diff --git a/multipart/tests/test_multipart.py b/multipart/tests/test_multipart.py index 7e49e16..811a640 100644 --- a/multipart/tests/test_multipart.py +++ b/multipart/tests/test_multipart.py @@ -12,7 +12,6 @@ unittest, ) from io import BytesIO -from six import binary_type, text_type try: from unittest.mock import MagicMock, Mock, patch diff --git a/requirements.txt b/requirements.txt index 37dc501..b2392b7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,4 +8,3 @@ pluggy==1.0.0 py==1.10.0 pytest==6.2.5 PyYAML==5.1 -six==1.11.0 diff --git a/setup.py b/setup.py index dd7542e..6cc3a20 100755 --- a/setup.py +++ b/setup.py @@ -25,9 +25,6 @@ license='Apache', platforms='any', zip_safe=False, - install_requires=[ - 'six>=1.4.0', - ], tests_require=tests_require, packages=[ 'multipart', From e12bd75497683a23e42710b4918ac11f1ff9f4d7 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <hugovk@users.noreply.github.com> Date: Tue, 23 Nov 2021 17:43:15 +0200 Subject: [PATCH 10/12] Tidy Python 3+ imports --- docs_requirements.txt | 1 - multipart/multipart.py | 5 ----- multipart/tests/compat.py | 5 ----- multipart/tests/test_multipart.py | 8 ++------ requirements.txt | 1 - tox.ini | 1 - 6 files changed, 2 insertions(+), 19 deletions(-) diff --git a/docs_requirements.txt b/docs_requirements.txt index 55b9125..9cebf18 100644 --- a/docs_requirements.txt +++ b/docs_requirements.txt @@ -7,7 +7,6 @@ coverage==3.6 distribute==0.6.34 docutils==0.10 invoke==0.2.0 -mock==1.0.1 pexpect-u==2.5.1 py==1.10.0 pytest==6.2.5 diff --git a/multipart/multipart.py b/multipart/multipart.py index e66c538..df5d374 100644 --- a/multipart/multipart.py +++ b/multipart/multipart.py @@ -1,11 +1,6 @@ from .decoders import * from .exceptions import * -try: - from urlparse import parse_qs -except ImportError: - from urllib.parse import parse_qs - import os import re import sys diff --git a/multipart/tests/compat.py b/multipart/tests/compat.py index 0fb84c3..897188d 100644 --- a/multipart/tests/compat.py +++ b/multipart/tests/compat.py @@ -1,8 +1,3 @@ -try: - import unittest2 as unittest -except ImportError: - import unittest - import os import re import sys diff --git a/multipart/tests/test_multipart.py b/multipart/tests/test_multipart.py index 811a640..0f14d95 100644 --- a/multipart/tests/test_multipart.py +++ b/multipart/tests/test_multipart.py @@ -5,18 +5,14 @@ import base64 import random import tempfile +import unittest from .compat import ( parametrize, parametrize_class, slow_test, - unittest, ) from io import BytesIO - -try: - from unittest.mock import MagicMock, Mock, patch -except ImportError: - from unittest.mock import MagicMock, Mock, patch +from unittest.mock import MagicMock, Mock, patch from ..multipart import * diff --git a/requirements.txt b/requirements.txt index b2392b7..82ac3e3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,6 @@ atomicwrites==1.2.1 attrs==19.2.0 coverage==4.5.1 -mock==2.0.0 more-itertools==4.3.0 pbr==4.3.0 pluggy==1.0.0 diff --git a/tox.ini b/tox.ini index c62cdb1..2072d7f 100644 --- a/tox.ini +++ b/tox.ini @@ -6,7 +6,6 @@ deps= pytest pytest-cov pytest-timeout - mock PyYAML commands= pytest --cov-report term-missing --cov-config .coveragerc --cov multipart --timeout=30 multipart/tests From c37ac893fbe66ad3fa3dabc1fdefed35638b1ef4 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <hugovk@users.noreply.github.com> Date: Tue, 23 Nov 2021 17:58:52 +0200 Subject: [PATCH 11/12] http to https --- LICENSE.txt | 2 +- README.rst | 2 +- docs/Makefile | 2 +- docs/make.bat | 2 +- docs/source/_themes/flask/layout.html | 2 +- docs/source/_themes/flask_small/layout.html | 4 ++-- docs/source/conf.py | 2 +- setup.py | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/LICENSE.txt b/LICENSE.txt index 7ff9d40..303a1bf 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -4,7 +4,7 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/README.rst b/README.rst index aceb6e2..8f8521b 100644 --- a/README.rst +++ b/README.rst @@ -10,7 +10,7 @@ python-multipart is an Apache2 licensed streaming multipart parser for Python. Test coverage is currently 100%. Documentation is available `here`_. -.. _here: http://andrew-d.github.io/python-multipart/ +.. _here: https://andrew-d.github.io/python-multipart/ Why? ---- diff --git a/docs/Makefile b/docs/Makefile index 9a2fbbd..c045a56 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -9,7 +9,7 @@ BUILDDIR = build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) -$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from https://sphinx-doc.org/) endif # Internal variables. diff --git a/docs/make.bat b/docs/make.bat index ce7daf8..10fdc25 100644 --- a/docs/make.bat +++ b/docs/make.bat @@ -56,7 +56,7 @@ if errorlevel 9009 ( echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ + echo.https://sphinx-doc.org/ exit /b 1 ) diff --git a/docs/source/_themes/flask/layout.html b/docs/source/_themes/flask/layout.html index 5caa4e2..6a80763 100644 --- a/docs/source/_themes/flask/layout.html +++ b/docs/source/_themes/flask/layout.html @@ -17,7 +17,7 @@ {%- block footer %} <div class="footer"> © Copyright {{ copyright }}. - Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>. + Created using <a href="https://www.sphinx-doc.org/">Sphinx</a>. </div> {% if pagename == 'index' %} </div> diff --git a/docs/source/_themes/flask_small/layout.html b/docs/source/_themes/flask_small/layout.html index aa1716a..83e5213 100644 --- a/docs/source/_themes/flask_small/layout.html +++ b/docs/source/_themes/flask_small/layout.html @@ -14,8 +14,8 @@ {% block relbar1 %}{% endblock %} {% block relbar2 %} {% if theme_github_fork %} - <a href="http://github.com/{{ theme_github_fork }}"><img style="position: fixed; top: 0; right: 0; border: 0;" - src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" /></a> + <a href="https://github.com/{{ theme_github_fork }}"><img style="position: fixed; top: 0; right: 0; border: 0;" + src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" /></a> {% endif %} {% endblock %} {% block sidebar1 %}{% endblock %} diff --git a/docs/source/conf.py b/docs/source/conf.py index 8f1e0ae..d13072e 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -253,4 +253,4 @@ # Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'http://docs.python.org/': None} +intersphinx_mapping = {'https://docs.python.org/': None} diff --git a/setup.py b/setup.py index 6cc3a20..fa45e1d 100755 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ version=version, description='A streaming multipart parser for Python', author='Andrew Dunham', - url='http://github.com/andrew-d/python-multipart', + url='https://github.com/andrew-d/python-multipart', license='Apache', platforms='any', zip_safe=False, From 98c852f926ad592fe88220dde5eff4f8955e9cde Mon Sep 17 00:00:00 2001 From: Tom Christie <tom@tomchristie.com> Date: Thu, 3 Nov 2022 11:54:55 +0000 Subject: [PATCH 12/12] Update docs_requirements.txt --- docs_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs_requirements.txt b/docs_requirements.txt index 9cebf18..ee47591 100644 --- a/docs_requirements.txt +++ b/docs_requirements.txt @@ -9,7 +9,7 @@ docutils==0.10 invoke==0.2.0 pexpect-u==2.5.1 py==1.10.0 -pytest==6.2.5 +pytest==6.2.6 pytest-capturelog==0.7 pytest-cov==1.6 pytest-timeout==0.3