From cf7552e9b6fcb7360859f3d4be55c8cf08383bd2 Mon Sep 17 00:00:00 2001 From: Ted Brookings Date: Wed, 15 Nov 2023 13:37:18 -0500 Subject: [PATCH] Bump mypy and pytest, fix simple mypy issues --- fgpyo/io/tests/test_io.py | 10 +-- fgpyo/util/logging.py | 6 +- fgpyo/util/metric.py | 4 +- fgpyo/util/types.py | 25 +++----- fgpyo/vcf/tests/test_builder.py | 13 ++-- poetry.lock | 110 +++++++++++++++++++++----------- pyproject.toml | 6 +- 7 files changed, 107 insertions(+), 67 deletions(-) diff --git a/fgpyo/io/tests/test_io.py b/fgpyo/io/tests/test_io.py index 1e4bc559..d4cf286d 100644 --- a/fgpyo/io/tests/test_io.py +++ b/fgpyo/io/tests/test_io.py @@ -80,14 +80,14 @@ def test_assert_path_is_writeable_pass() -> None: """Should return the correct writeable path""" with NamedTemp(suffix=".txt", mode="w", delete=True) as read_file: path = Path(read_file.name) - assert fio.assert_path_is_writeable(path=path) is None + fio.assert_path_is_writeable(path=path) @pytest.mark.parametrize( "suffix, expected", [ - (".gz", io._io.TextIOWrapper), - (".fa", io._io.TextIOWrapper), + (".gz", io.TextIOWrapper), + (".fa", io.TextIOWrapper), ], ) def test_reader( @@ -103,8 +103,8 @@ def test_reader( @pytest.mark.parametrize( "suffix, expected", [ - (".gz", io._io.TextIOWrapper), - (".fa", io._io.TextIOWrapper), + (".gz", io.TextIOWrapper), + (".fa", io.TextIOWrapper), ], ) def test_writer( diff --git a/fgpyo/util/logging.py b/fgpyo/util/logging.py index b6aff6fc..97174fe6 100644 --- a/fgpyo/util/logging.py +++ b/fgpyo/util/logging.py @@ -34,11 +34,13 @@ """ import logging +import sys -try: # py>=38 +if sys.version_info >= (3, 8): from typing import Literal -except ImportError: # py<38 +else: from typing_extensions import Literal + import socket from contextlib import AbstractContextManager from logging import Logger diff --git a/fgpyo/util/metric.py b/fgpyo/util/metric.py index a4681994..4da45098 100644 --- a/fgpyo/util/metric.py +++ b/fgpyo/util/metric.py @@ -137,7 +137,7 @@ def values(self) -> Iterator[Any]: """An iterator over attribute values in the same order as the header.""" return iter(attr.astuple(self, recurse=False)) - def formatted_values(self) -> Iterator[str]: + def formatted_values(self) -> List[str]: """An iterator over formatted attribute values in the same order as the header.""" return [self.format_value(value) for value in self.values()] @@ -226,7 +226,7 @@ def parse(cls, fields: List[str]) -> Any: return inspect.attr_from(cls=cls, kwargs=dict(zip(header, fields)), parsers=parsers) @classmethod - def write(cls, path: Path, *values: MetricType) -> None: + def write(cls, path: Path, *values: "Metric") -> None: """Writes zero or more metrics to the given path. The header will always be written. diff --git a/fgpyo/util/types.py b/fgpyo/util/types.py index ea55d5c1..14412cf0 100644 --- a/fgpyo/util/types.py +++ b/fgpyo/util/types.py @@ -1,5 +1,6 @@ import collections import inspect +import sys import typing from enum import Enum from functools import partial @@ -9,20 +10,19 @@ from typing import TypeVar from typing import Union -try: +# `get_origin_type` is a method that gets the outer type (ex list in a List[str]) +# `get_arg_types` is a method that gets the inner type (ex str in a List[str]) +if sys.version_info >= (3, 8): from typing import Literal -except ImportError: - from typing_extensions import Literal - -# `get_origin_type` is a method that gets the outer type (ex list in a List[str]) -if hasattr(typing, "get_origin"): # py>=38 get_origin_type = typing.get_origin -else: # py<38 + get_arg_types = typing.get_args +else: + import typing_inspect + from typing_extensions import Literal def get_origin_type(tp: Type) -> Type: """Returns the outer type of a Typing object (ex list in a List[T])""" - import typing_inspect if type(tp) is type(Literal): # Py<=3.6. return Literal @@ -37,15 +37,8 @@ def get_origin_type(tp: Type) -> Type: typing.Dict: dict, }.get(origin, origin) - -# `get_origin_type` is a method that gets the inner type (ex str in a List[str]) -if hasattr(typing, "get_args"): # py>=38 - get_arg_types = typing.get_args -else: # py<38 - def get_arg_types(tp: Type) -> Type: """Gets the inner types of a Typing object (ex T in a List[T])""" - import typing_inspect if type(tp) is type(Literal): # Py<=3.6. return tp.__values__ @@ -122,7 +115,7 @@ def _make_union_parser_worker( union: Type[UnionType], parsers: Iterable[Callable[[str], UnionType]], value: str, -) -> T: +) -> UnionType: """Worker function behind union parsing. Iterates through possible parsers for the union and returns the value produced by the first parser that works. Otherwise raises an error if none work""" diff --git a/fgpyo/vcf/tests/test_builder.py b/fgpyo/vcf/tests/test_builder.py index 20c63c36..58f39752 100644 --- a/fgpyo/vcf/tests/test_builder.py +++ b/fgpyo/vcf/tests/test_builder.py @@ -5,6 +5,7 @@ from typing import Any from typing import Dict from typing import Iterable +from typing import List from typing import Mapping from typing import Tuple @@ -33,7 +34,7 @@ def sequence_dict() -> Dict[str, Dict[str, Any]]: def _get_random_contig( random_generator: random.Random, sequence_dict: Dict[str, Dict[str, Any]] -) -> (str, int): +) -> Tuple[str, int]: """Randomly select a contig from the sequence dictionary and return its name and length.""" contig = random_generator.choice(list(sequence_dict.values())) return contig["ID"], contig["length"] @@ -102,7 +103,7 @@ def _get_random_variant_inputs( @pytest.fixture(scope="function") def zero_sample_record_inputs( random_generator: random.Random, sequence_dict: Dict[str, Dict[str, Any]] -) -> Tuple[Mapping[str, Any]]: +) -> Tuple[Mapping[str, Any], ...]: """ Fixture with inputs to create test Variant records for zero-sample VCFs (no genotypes). Make them MappingProxyType so that they are immutable. @@ -174,7 +175,7 @@ def test_minimal_inputs() -> None: def test_sort_order(random_generator: random.Random) -> None: """Test if the VariantBuilder sorts the Variant records in the correct order.""" - sorted_inputs = [ + sorted_inputs: List[Dict[str, Any]] = [ {"contig": "chr1", "pos": 100}, {"contig": "chr1", "pos": 500}, {"contig": "chr2", "pos": 1000}, @@ -183,7 +184,9 @@ def test_sort_order(random_generator: random.Random) -> None: {"contig": "chr10", "pos": 20}, {"contig": "chr11", "pos": 5}, ] - scrambled_inputs = random_generator.sample(sorted_inputs, k=len(sorted_inputs)) + scrambled_inputs: List[Dict[str, Any]] = random_generator.sample( + sorted_inputs, k=len(sorted_inputs) + ) assert scrambled_inputs != sorted_inputs # there should be something to actually sort variant_builder = VariantBuilder() for record_input in scrambled_inputs: @@ -223,7 +226,7 @@ def _get_is_compressed(input_file: Path) -> bool: @pytest.mark.parametrize("compress", (True, False)) def test_zero_sample_vcf_round_trip( temp_path: Path, - zero_sample_record_inputs, + zero_sample_record_inputs: Tuple[Mapping[str, Any], ...], compress: bool, ) -> None: """ diff --git a/poetry.lock b/poetry.lock index f49531f5..b4360a92 100644 --- a/poetry.lock +++ b/poetry.lock @@ -22,16 +22,6 @@ files = [ {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, ] -[[package]] -name = "atomicwrites" -version = "1.4.1" -description = "Atomic file writes." -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"}, -] - [[package]] name = "attrs" version = "22.1.0" @@ -211,6 +201,20 @@ files = [ {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, ] +[[package]] +name = "exceptiongroup" +version = "1.1.3" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, + {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, +] + +[package.extras] +test = ["pytest (>=6)"] + [[package]] name = "flake8" version = "5.0.4" @@ -462,15 +466,62 @@ dmypy = ["psutil (>=4.0)"] python2 = ["typed-ast (>=1.4.0,<2)"] reports = ["lxml"] +[[package]] +name = "mypy" +version = "1.7.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5da84d7bf257fd8f66b4f759a904fd2c5a765f70d8b52dde62b521972a0a2357"}, + {file = "mypy-1.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a3637c03f4025f6405737570d6cbfa4f1400eb3c649317634d273687a09ffc2f"}, + {file = "mypy-1.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b633f188fc5ae1b6edca39dae566974d7ef4e9aaaae00bc36efe1f855e5173ac"}, + {file = "mypy-1.7.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d6ed9a3997b90c6f891138e3f83fb8f475c74db4ccaa942a1c7bf99e83a989a1"}, + {file = "mypy-1.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:1fe46e96ae319df21359c8db77e1aecac8e5949da4773c0274c0ef3d8d1268a9"}, + {file = "mypy-1.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:df67fbeb666ee8828f675fee724cc2cbd2e4828cc3df56703e02fe6a421b7401"}, + {file = "mypy-1.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a79cdc12a02eb526d808a32a934c6fe6df07b05f3573d210e41808020aed8b5d"}, + {file = "mypy-1.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f65f385a6f43211effe8c682e8ec3f55d79391f70a201575def73d08db68ead1"}, + {file = "mypy-1.7.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e81ffd120ee24959b449b647c4b2fbfcf8acf3465e082b8d58fd6c4c2b27e46"}, + {file = "mypy-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:f29386804c3577c83d76520abf18cfcd7d68264c7e431c5907d250ab502658ee"}, + {file = "mypy-1.7.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:87c076c174e2c7ef8ab416c4e252d94c08cd4980a10967754f91571070bf5fbe"}, + {file = "mypy-1.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6cb8d5f6d0fcd9e708bb190b224089e45902cacef6f6915481806b0c77f7786d"}, + {file = "mypy-1.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d93e76c2256aa50d9c82a88e2f569232e9862c9982095f6d54e13509f01222fc"}, + {file = "mypy-1.7.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cddee95dea7990e2215576fae95f6b78a8c12f4c089d7e4367564704e99118d3"}, + {file = "mypy-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:d01921dbd691c4061a3e2ecdbfbfad029410c5c2b1ee88946bf45c62c6c91210"}, + {file = "mypy-1.7.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:185cff9b9a7fec1f9f7d8352dff8a4c713b2e3eea9c6c4b5ff7f0edf46b91e41"}, + {file = "mypy-1.7.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7a7b1e399c47b18feb6f8ad4a3eef3813e28c1e871ea7d4ea5d444b2ac03c418"}, + {file = "mypy-1.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc9fe455ad58a20ec68599139ed1113b21f977b536a91b42bef3ffed5cce7391"}, + {file = "mypy-1.7.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d0fa29919d2e720c8dbaf07d5578f93d7b313c3e9954c8ec05b6d83da592e5d9"}, + {file = "mypy-1.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:2b53655a295c1ed1af9e96b462a736bf083adba7b314ae775563e3fb4e6795f5"}, + {file = "mypy-1.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c1b06b4b109e342f7dccc9efda965fc3970a604db70f8560ddfdee7ef19afb05"}, + {file = "mypy-1.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bf7a2f0a6907f231d5e41adba1a82d7d88cf1f61a70335889412dec99feeb0f8"}, + {file = "mypy-1.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:551d4a0cdcbd1d2cccdcc7cb516bb4ae888794929f5b040bb51aae1846062901"}, + {file = "mypy-1.7.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:55d28d7963bef00c330cb6461db80b0b72afe2f3c4e2963c99517cf06454e665"}, + {file = "mypy-1.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:870bd1ffc8a5862e593185a4c169804f2744112b4a7c55b93eb50f48e7a77010"}, + {file = "mypy-1.7.0-py3-none-any.whl", hash = "sha256:96650d9a4c651bc2a4991cf46f100973f656d69edc7faf91844e87fe627f7e96"}, + {file = "mypy-1.7.0.tar.gz", hash = "sha256:1e280b5697202efa698372d2f39e9a6713a0395a756b1c6bd48995f8d72690dc"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=4.1.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + [[package]] name = "mypy-extensions" -version = "0.4.3" -description = "Experimental type system extensions for programs checked with the mypy typechecker." +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." optional = false -python-versions = "*" +python-versions = ">=3.5" files = [ - {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, - {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] [[package]] @@ -516,17 +567,6 @@ importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] -[[package]] -name = "py" -version = "1.11.0" -description = "library with cross-python path, ini-parsing, io, code, log facilities" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, - {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, -] - [[package]] name = "pycodestyle" version = "2.9.1" @@ -637,28 +677,26 @@ files = [ [[package]] name = "pytest" -version = "7.0.1" +version = "7.4.3" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "pytest-7.0.1-py3-none-any.whl", hash = "sha256:9ce3ff477af913ecf6321fe337b93a2c0dcf2a0a1439c43f5452112c1e4280db"}, - {file = "pytest-7.0.1.tar.gz", hash = "sha256:e30905a0c131d3d94b89624a1cc5afec3e0ba2fbdb151867d8e0ebd49850f171"}, + {file = "pytest-7.4.3-py3-none-any.whl", hash = "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac"}, + {file = "pytest-7.4.3.tar.gz", hash = "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"}, ] [package.dependencies] -atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} -attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" -py = ">=1.8.2" -tomli = ">=1.0.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] -testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-cov" @@ -1109,4 +1147,4 @@ docs = ["sphinx", "sphinx_rtd_theme"] [metadata] lock-version = "2.0" python-versions = ">=3.7.0,<4.0" -content-hash = "5e1d5ff1445474f8dfe2e508fe67f20feb7dd02642d188f1e657a348bc333fd0" +content-hash = "51b3d80eacacfbee5a830d83dc79fd63c82fe66f411d456ad79983babd93fe7d" diff --git a/pyproject.toml b/pyproject.toml index 783be7c5..1869aae0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,7 @@ typing_inspect = { version = ">=0.3.1", python = "<3.8" } # inspecting types sphinx = {version = "4.3.1", optional = true} sphinx_rtd_theme = {version = "^1.3.0", optional = true} pysam = ">=0.22.0" +pytest = ">=7.4.0" [tool.poetry.extras] docs = ["sphinx", "sphinx_rtd_theme"] @@ -39,7 +40,10 @@ docs = ["sphinx", "sphinx_rtd_theme"] [tool.poetry.dev-dependencies] setuptools = ">=68.0.0" pytest = ">=5.4.2" -mypy = ">=0.770" +mypy = [ + { version = ">=0.770", python = "<3.8" }, + { version = ">=1.7.0", python = ">=3.8"} +] flake8 = [ { version = ">=3.8.1", python = "<3.12.0" }, { version = ">=6.1.0", python = ">=3.12.0" },