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",