diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b58a43d6..f3c93561 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -35,39 +35,10 @@ jobs: - macos-latest - windows-latest python: - - '3.6' - - '3.7' - '3.8' - '3.9' - '3.10' - # Python 3.6 is not available on Ubuntu 22.04, which is what - # ubuntu-latest points to as of Q4 2022¹, so replace that job with 3.6 - # on Ubuntu 20.04. - # -trs, 22 Nov 2022 - # - # ¹ - # - # A bodged https://github.com/actions/python-version build of 3.7.17 - # for macOS mistakenly doesn't include the bz2 module of the stdlib¹, - # which breaks us because some of our deps (fsspec, at the least) - # assume bz2 is available² (though that will be fixed in its next - # release³). Replace the 3.7 macOS job with a 3.7.16 job. - # -trs, 22 June 2023 - # - # ¹ - # ² - # ³ - exclude: - - os: ubuntu-latest - python: '3.6' - - os: macos-latest - python: '3.7' - include: - - os: ubuntu-20.04 - python: '3.6' - - os: macos-latest - python: '3.7.16' runs-on: ${{ matrix.os }} defaults: run: @@ -240,11 +211,9 @@ jobs: - macos-latest - windows-latest python: - - '3.6' - - '3.7' - '3.8' - '3.9' - # XXX TODO: Add 3.10 here once supported by Conda/Bioconda/Conda Forge. + - '3.10' runs-on: ${{ matrix.os }} defaults: run: diff --git a/CHANGES.md b/CHANGES.md index 1d3a7c7f..71ef9e6e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,6 +13,9 @@ development source code and as such may not be routinely kept up to date. # __NEXT__ +This release drops support for Python versions 3.6 and 3.7. +([#325](https://github.com/nextstrain/cli/pull/325)) + ## Improvements * `nextstrain remote upload` now skips gzip compression when uploading diff --git a/doc/development.md b/doc/development.md index d8c1c9b1..4b2c75be 100644 --- a/doc/development.md +++ b/doc/development.md @@ -2,7 +2,7 @@ Development of `nextstrain-cli` happens at . -We currently target compatibility with Python 3.6 and higher. +We currently target compatibility with Python 3.8 and higher. Versions for this project follow the [Semantic Versioning rules][]. diff --git a/doc/installation.rst b/doc/installation.rst index f4e46001..34c67183 100644 --- a/doc/installation.rst +++ b/doc/installation.rst @@ -42,7 +42,7 @@ From PyPI --------- .. note:: - Nextstrain CLI is written in Python 3 and requires at least Python 3.6. There + Nextstrain CLI is written in Python 3 and requires at least Python 3.8. There are many ways to install Python 3 on Windows, macOS, or Linux, including the `official packages`_, `Homebrew`_ for macOS, and the `Anaconda Distribution`_. Details are beyond the scope of this guide, but make sure you install Python diff --git a/mypy.ini b/mypy.ini index adf1f97f..d1b4d387 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,6 +1,6 @@ [mypy] -# We currently aim for compat with 3.6. -python_version = 3.6 +# We currently aim for compat with 3.8. +python_version = 3.8 namespace_packages = True # Check function bodies which don't have a typed signature. This prevents a @@ -31,9 +31,6 @@ ignore_missing_imports = True [mypy-importlib.metadata] ignore_missing_imports = True -[mypy-importlib_metadata] -ignore_missing_imports = True - [mypy-importlib.resources] ignore_missing_imports = True diff --git a/nextstrain/cli/__main__.py b/nextstrain/cli/__main__.py index 3dbda8ba..bd634f35 100644 --- a/nextstrain/cli/__main__.py +++ b/nextstrain/cli/__main__.py @@ -13,41 +13,29 @@ def main(): # rest of the codebase. Avoids needing to instruct folks to set # PYTHONIOENCODING=UTF-8 or use Python's UTF-8 mode (-X utf8 or # PYTHONUTF8=1). - sys.stdout = reconfigure_stdio(sys.stdout) # type: ignore[arg-type] - sys.stderr = reconfigure_stdio(sys.stderr) # type: ignore[arg-type] + reconfigure_stdio(sys.stdout) # type: ignore[arg-type] + reconfigure_stdio(sys.stderr) # type: ignore[arg-type] return cli.run( argv[1:] ) -def reconfigure_stdio(stdio: TextIOWrapper) -> TextIOWrapper: +def reconfigure_stdio(stdio: TextIOWrapper): """ Reconfigure *stdio* to match the assumptions of this codebase. Suitable only for output streams (e.g. stdout, stderr), as reconfiguring an input stream is more complicated. """ - # XXX TODO: When we drop Python 3.6 support, most of this function can be - # replaced by stdio.reconfigure(). - # -trs, 6 June 2022 - - # Flush any pending output under old configuration. - stdio.flush() # Configure new text stream on the same underlying buffered byte stream. - return TextIOWrapper( - stdio.buffer, - + stdio.reconfigure( # Always use UTF-8 and be more lenient on stderr so even mangled error # messages make it out. encoding = "UTF-8", errors = "backslashreplace" if stdio is sys.stderr else "strict", # Explicitly enable universal newlines mode so we do the right thing. - newline = None, - - # Preserve line buffering which is set at process start dynamically - # depending on what the stdio is actually attached to. - line_buffering = stdio.line_buffering) + newline = None) # Run when called as `python -m nextstrain.cli`, here for good measure. diff --git a/nextstrain/cli/aws/cognito/__init__.py b/nextstrain/cli/aws/cognito/__init__.py index 209361c9..94c20ef9 100644 --- a/nextstrain/cli/aws/cognito/__init__.py +++ b/nextstrain/cli/aws/cognito/__init__.py @@ -2,17 +2,8 @@ AWS Cognito helpers. """ import boto3 -import warnings - -# Ignore noisy warning from cryptography 37.0.0 and 39.0.0 about deprecated support for Python 3.6. -with warnings.catch_warnings(): - warnings.filterwarnings( - "ignore", - message = "Python 3\\.6 is no longer supported by the Python core team\\. Therefore, support for it is deprecated in cryptography", - category = UserWarning) - - import jwt - import jwt.exceptions +import jwt +import jwt.exceptions from .srp import CognitoSRP, NewPasswordRequiredError # noqa: F401 (NewPasswordRequiredError is for re-export) diff --git a/nextstrain/cli/command/build.py b/nextstrain/cli/command/build.py index c8391fd1..b5a61bf8 100644 --- a/nextstrain/cli/command/build.py +++ b/nextstrain/cli/command/build.py @@ -253,10 +253,6 @@ def parse_snakemake_args(args): >>> sorted(parse_snakemake_args([]).items()) [('--cores', []), ('--resources', [])] """ - # XXX TODO: Consider using a small ArgumentParser() for this in the - # future, when we can require Python 3.7 and use parse_intermixed_args(). - # -trs, 20 May 2020 - opts = { "-j" if re.search(r"^-j\d+$", arg) else arg for arg in map(lambda arg: arg.split("=", 1)[0], args) diff --git a/nextstrain/cli/util.py b/nextstrain/cli/util.py index 18c3dff7..14efd8ac 100644 --- a/nextstrain/cli/util.py +++ b/nextstrain/cli/util.py @@ -1,8 +1,3 @@ -try: - from importlib.metadata import distribution as distribution_info, PackageNotFoundError -except ModuleNotFoundError: - from importlib_metadata import distribution as distribution_info, PackageNotFoundError - import os import platform import re @@ -11,6 +6,7 @@ import subprocess import sys from functools import partial +from importlib.metadata import distribution as distribution_info, PackageNotFoundError from typing import Any, Callable, Iterable, Mapping, List, Optional, Sequence, Tuple, Union, overload # TODO: Use typing.Literal once Python 3.8 is the minimum supported version. from typing_extensions import Literal @@ -271,10 +267,6 @@ def capture_output(argv, extra_env: Mapping = {}): Run the command specified by the argument list and return a list of output lines. - This wrapper around subprocess.run() exists because its own capture_output - parameter wasn't added until Python 3.7 and we aim for compat with 3.6. - When we bump our minimum Python version, we can remove this wrapper. - If an *extra_env* mapping is passed, the provided keys and values are overlayed onto the current environment. Keys with a value of ``None`` are removed from the current environment (i.e. like ``del os.environ[key]``). diff --git a/pyrightconfig.json b/pyrightconfig.json index b0631811..43fc4cb9 100644 --- a/pyrightconfig.json +++ b/pyrightconfig.json @@ -1,5 +1,5 @@ { - "pythonVersion": "3.6", + "pythonVersion": "3.8", "include": ["nextstrain"], "ignore": [ "nextstrain/cli/markdown.py", diff --git a/setup.py b/setup.py index 9936e800..29f8f1f6 100644 --- a/setup.py +++ b/setup.py @@ -70,7 +70,9 @@ def find_namespaced_packages(namespace): # Python 3 only "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", ], # Install a "nextstrain" program which calls nextstrain.cli.__main__.main() @@ -81,13 +83,11 @@ def find_namespaced_packages(namespace): ], }, - python_requires = '>=3.6', + python_requires = '>=3.8', install_requires = [ - "dataclasses; python_version < '3.7'", "docutils", "fasteners", - "importlib_metadata; python_version < '3.8'", "importlib_resources >=5.3.0; python_version < '3.11'", "packaging", "pyjwt[crypto] >=2.0.0",