From b8ac915f32ccc51a09607f556e7ce9286cd6055f Mon Sep 17 00:00:00 2001 From: Ted Brookings Date: Wed, 22 Nov 2023 15:16:13 -0500 Subject: [PATCH] Fix type problems found by latest mypy (#75) Closes #73 * Move to current latest version of pytest to avoid deprecation warning * Move to current latest version of mypy * Correct multiple problems with type and import flagged by newer mypy --- fgpyo/io/tests/test_io.py | 10 +-- fgpyo/util/inspect.py | 29 +++++---- fgpyo/util/logging.py | 6 +- fgpyo/util/metric.py | 4 +- fgpyo/util/types.py | 44 ++++++------- fgpyo/vcf/tests/test_builder.py | 13 ++-- poetry.lock | 110 +++++++++++++++++++++----------- pyproject.toml | 6 +- 8 files changed, 134 insertions(+), 88 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/inspect.py b/fgpyo/util/inspect.py index 6a69a6b5..5ab8a4e8 100644 --- a/fgpyo/util/inspect.py +++ b/fgpyo/util/inspect.py @@ -1,14 +1,20 @@ +import sys from typing import Any from typing import Dict +from typing import Iterable from typing import List from typing import Tuple from typing import Type from typing import Union -try: # py>=38 +if sys.version_info >= (3, 8): from typing import Literal -except ImportError: # py<38 +else: from typing_extensions import Literal +if sys.version_info >= (3, 12): + from typing import TypeAlias +else: + from typing_extensions import TypeAlias import functools from enum import Enum @@ -29,8 +35,8 @@ class ParserNotFoundException(Exception): def split_at_given_level( field: str, split_delim: str = ",", - increase_depth_chars: List[str] = ["{", "(", "["], - decrease_depth_chars: List[str] = ["}", ")", "]"], + increase_depth_chars: Iterable[str] = ("{", "(", "["), + decrease_depth_chars: Iterable[str] = ("}", ")", "]"), ) -> List[str]: """ Splits a nested field by its outer-most level @@ -65,7 +71,7 @@ def split_at_given_level( def _get_parser( - cls: Type, type_: Type, parsers: Optional[Dict[type, Callable[[str], Any]]] = None + cls: Type, type_: TypeAlias, parsers: Optional[Dict[type, Callable[[str], Any]]] = None ) -> partial: """Attempts to find a parser for a provided type. @@ -114,14 +120,13 @@ def get_parser() -> partial: subtypes[0], parsers, ) - origin_type = types.get_origin_type(type_) return functools.partial( - lambda s: origin_type( + lambda s: list( [] if s == "" else [ subtype_parser(item) - for item in origin_type(split_at_given_level(s, split_delim=",")) + for item in list(split_at_given_level(s, split_delim=",")) ] ) ) @@ -135,14 +140,13 @@ def get_parser() -> partial: subtypes[0], parsers, ) - origin_type = types.get_origin_type(type_) return functools.partial( - lambda s: origin_type( + lambda s: set( set({}) if s == "{}" else [ subtype_parser(item) - for item in origin_type(split_at_given_level(s[1:-1], split_delim=",")) + for item in set(split_at_given_level(s[1:-1], split_delim=",")) ] ) ) @@ -155,7 +159,6 @@ def get_parser() -> partial: ) for subtype in types.get_arg_types(type_) ] - origin_type = types.get_origin_type(type_) def tuple_parse(tuple_string: str) -> Tuple[Any, ...]: """ @@ -247,7 +250,7 @@ def dict_parse(dict_string: str) -> Dict[Any, Any]: # Set the name that the user expects to see in error messages (we always # return a temporary partial object so it's safe to set its __name__). # Unions and Literals don't have a __name__, but their str is fine. - parser.__name__ = getattr(type_, "__name__", str(type_)) + setattr(parser, "__name__", getattr(type_, "__name__", str(type_))) return parser 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..9cda23ee 100644 --- a/fgpyo/util/metric.py +++ b/fgpyo/util/metric.py @@ -118,7 +118,7 @@ from fgpyo import io from fgpyo.util import inspect -MetricType = TypeVar("MetricType") +MetricType = TypeVar("MetricType", bound="Metric") @attr.s @@ -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()] diff --git a/fgpyo/util/types.py b/fgpyo/util/types.py index ea55d5c1..822c4dc2 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,25 +37,19 @@ 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__ return typing_inspect.get_args(tp, evaluate=True) # evaluate=True default on Py>=3.7. -T = TypeVar("T") UnionType = TypeVar("UnionType", bound="Union") EnumType = TypeVar("EnumType", bound="Enum") -LiteralType = TypeVar("LiteralType", bound="Literal") +# conceptually bound to "Literal" but that's not valid in the spec +# see: https://peps.python.org/pep-0586/#illegal-parameters-for-literal-at-type-check-time +LiteralType = TypeVar("LiteralType") class InspectException(Exception): @@ -92,7 +86,7 @@ def make_enum_parser(enum: Type[EnumType]) -> partial: return partial(_make_enum_parser_worker, enum) -def is_constructible_from_str(type_: T) -> bool: +def is_constructible_from_str(type_: type) -> bool: """Returns true if the provided type can be constructed from a string""" try: sig = inspect.signature(type_) @@ -113,7 +107,7 @@ def is_constructible_from_str(type_: T) -> bool: return False -def _is_optional(type_: T) -> bool: +def _is_optional(type_: type) -> bool: """Returns true if type_ is optional""" return get_origin_type(type_) is Union and type(None) in get_arg_types(type_) @@ -122,7 +116,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""" @@ -130,7 +124,9 @@ def _make_union_parser_worker( # 'None' instead of the object None if _is_optional(union): try: - return none_parser(value) + # mypy doesn't like functions that return None always, so return separately + none_parser(value) + return None except (ValueError, InspectException): pass for p in parsers: @@ -141,7 +137,7 @@ def _make_union_parser_worker( raise ValueError(f"{value} could not be parsed as any of {union}") -def make_union_parser(union: Type[UnionType], parsers: Iterable[Callable[[str], T]]) -> partial: +def make_union_parser(union: Type[UnionType], parsers: Iterable[Callable[[str], type]]) -> partial: """Generates a parser function for a union type object and set of parsers for the possible parsers to that union type object """ @@ -176,12 +172,12 @@ def make_literal_parser( return partial(_make_literal_parser_worker, literal, parsers) -def is_list_like(type_: T) -> bool: +def is_list_like(type_: type) -> bool: """Returns true if the value is a list or list like object""" return get_origin_type(type_) in [list, collections.abc.Iterable, collections.abc.Sequence] -def none_parser(value: str) -> None: +def none_parser(value: str) -> Literal[None]: """Returns None if the value is 'None', else raises an error""" if value == "": return None 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" },