Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Python 3.8-3.10, drop EOL 2.7-3.5 #42

Merged
merged 12 commits into from
Nov 3, 2022
Merged
4 changes: 0 additions & 4 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
branch = False
# branch = True
omit =
*six.py
multipart/tests/*

[report]
Expand Down Expand Up @@ -33,6 +32,3 @@ exclude_lines =
# Ignore import exceptions
except ImportError:

# Ignore python compatibility.
if PY3

2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.6, 3.7, 3.8, 3.9]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm personally strongly against adding this, it would have prevented me testing the CI on my fork and is an extra impediment to contributing.

https://github.com/hugovk/python-multipart/actions

Unless I edited the yml in my fork, and then remembered to revert it before creating a PR.
Or I can submit a PR on my branch first to check it passes -> extra impediment.

Or I can develop on my master -> not a best practice, we should use feature branches, and I only have a single master -> extra impediment.

The fact it's tested on upstream and on the fork isn't such a big deal: the testing is split between two different accounts and therefore doesn't use up any extra quota/time from any single account.

CIs allow us to test on a much larger matrix of Python versions and operating systems, much more than is possible locally and sometimes faster too. I sometimes avoid contributing to projects with no/broken CI.

I believe the use of CIs are of huge benefit for software quality and development, especially when so easy nowadays; Travis CI really revolutionised this, remember how hard testing was before. We should rather encourage people to test on their own forks before creating PRs.

Fail fast: shorten the time to finding a failure, enable quicker loops of incremental development.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're not against this. You're against GitHub blocking first time contributors for running their pipeline.

There's a setting to allow old GitHub users (3 months old ?) to not be blocked.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're not against this. You're against GitHub blocking first time contributors for running their pipeline.

I don't mind that bit too much :)


Consider this workflow:

I come along and fork the repo and enable the CI on my fork.

I create a feature branch add-3.10.

I push my feature branch to my fork and check the CI.

I want the CI to test my changes on all supported Python versions (and possibly on several operating systems).

If it fails, I'll iterate: fix, push, repeat.

(This actually happened, see the first few failures as I iterated: https://github.com/hugovk/python-multipart/actions)

Once it passes, I can create a PR here, knowing that when the CI is enabled for the PR (we're 10 months and counting here), I'm confident it will pass and there's no surprises because I didn't test Python 3.7 or so on.

However, if we have these branch restrictions:

on:
  push:
    branches: ["master"]
  pull_request:
    branches: ["master"]

The CI won't build for me.

My options:

  • Don't test on the CI, hope for the best when the PR runs here
  • Edit the file and remove the restrictions, then before submitting the PR, remember to re-add the restrictions
  • Develop in my master branch - not recommended, you can only work on one thing

All this applies even if I'm a second-time contributor.

python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]

steps:
- uses: actions/checkout@v2
Expand Down
20 changes: 0 additions & 20 deletions .travis.yml

This file was deleted.

2 changes: 1 addition & 1 deletion LICENSE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
8 changes: 4 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
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.
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?
----
Expand All @@ -25,4 +25,4 @@ If you want to test:
.. code-block:: bash

$ pip install -r requirements.txt
$ py.test
$ pytest
2 changes: 1 addition & 1 deletion docs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion docs/make.bat
Original file line number Diff line number Diff line change
Expand Up @@ -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
)

Expand Down
2 changes: 1 addition & 1 deletion docs/source/_themes/flask/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
{%- block footer %}
<div class="footer">
&copy; 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>
Expand Down
4 changes: 2 additions & 2 deletions docs/source/_themes/flask_small/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -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 %}
Expand Down
20 changes: 9 additions & 11 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
#
# python-multipart documentation build configuration file, created by
# sphinx-quickstart on Fri Apr 5 20:24:27 2013.
#
Expand Down Expand Up @@ -42,8 +40,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
Expand Down Expand Up @@ -192,8 +190,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
Expand Down Expand Up @@ -222,8 +220,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.
Expand All @@ -236,8 +234,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'),
]

Expand All @@ -255,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}
3 changes: 1 addition & 2 deletions docs_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ 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==2.3.4
pytest==6.2.6
pytest-capturelog==0.7
pytest-cov==1.6
pytest-timeout==0.3
Expand Down
1 change: 0 additions & 1 deletion multipart/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from __future__ import absolute_import
import sys

# This is the canonical package information.
Expand Down
12 changes: 6 additions & 6 deletions multipart/decoders.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import base64
import binascii

from .exceptions import Base64Error, DecodeError
from .exceptions import 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
Expand Down Expand Up @@ -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.')

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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})"
12 changes: 0 additions & 12 deletions multipart/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
import binascii

from six import PY3


class FormParserError(ValueError):
"""Base error class for our form parser."""
pass
Expand Down Expand Up @@ -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
Loading