Skip to content

Commit cd554be

Browse files
authored
chore: Update mypy config (#134)
* chore: Update mypy config * chore: rm unused type ignores * chore: rm redundant cast * chore: ignore unreachable false positive * chore: fix any-return * chore: fix untyped-call * chore: fix no-any-return * doc: clarify cast
1 parent 520e508 commit cd554be

File tree

8 files changed

+47
-24
lines changed

8 files changed

+47
-24
lines changed

ci/mypy.ini

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
[mypy]
2-
strict_optional = False
3-
ignore_missing_imports = True
4-
disallow_untyped_decorators = False
5-
follow_imports = silent
6-
disallow_untyped_defs = True
2+
python_version = 3.8
3+
strict_optional = false
4+
check_untyped_defs = true
5+
disallow_incomplete_defs = true
6+
disallow_untyped_calls = true
7+
disallow_untyped_decorators = true
8+
disallow_untyped_defs = true
9+
no_implicit_optional = true
10+
warn_no_return = true
11+
warn_redundant_casts = true
12+
warn_return_any = true
13+
warn_unreachable = true
14+
warn_unused_configs = true
15+
warn_unused_ignores = true
16+

fgpyo/fastx/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
from typing import Tuple
3535
from typing import Type
3636
from typing import Union
37-
from typing import cast
3837

3938
from pysam import FastxFile
4039
from pysam import FastxRecord
@@ -77,7 +76,6 @@ def __next__(self) -> Tuple[FastxRecord, ...]:
7776
)
7877
)
7978
else:
80-
records = cast(Tuple[FastxRecord, ...], records)
8179
record_names: Set[str] = {self._name_minus_ordinal(record.name) for record in records}
8280
if len(record_names) != 1:
8381
raise ValueError(f"FASTX record names do not all match: {record_names}")

fgpyo/sam/__init__.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ def _pysam_open(
253253
:class:`~pysam.AlignmentFile`; may not include "mode".
254254
"""
255255

256-
if isinstance(path, (str, Path)): # type: ignore
256+
if isinstance(path, (str, Path)):
257257
if str(path) in _STDIN_PATHS and open_for_reading:
258258
path = sys.stdin
259259
elif str(path) in _STDOUT_PATHS and not open_for_reading:
@@ -505,23 +505,23 @@ def from_cigarstring(cls, cigarstring: str) -> "Cigar":
505505
i = 0
506506
while i < cigarstring_length:
507507
if not cigarstring[i].isdigit():
508-
raise cls._pretty_cigarstring_exception(cigarstring, i) # type: ignore
508+
raise cls._pretty_cigarstring_exception(cigarstring, i)
509509
length = int(cigarstring[i])
510510
i += 1
511511
while i < cigarstring_length and cigarstring[i].isdigit():
512512
length = (length * 10) + int(cigarstring[i])
513513
i += 1
514514
if i == cigarstring_length:
515-
raise cls._pretty_cigarstring_exception(cigarstring, i) # type: ignore
515+
raise cls._pretty_cigarstring_exception(cigarstring, i)
516516
try:
517517
operator = CigarOp.from_character(cigarstring[i])
518518
elements.append(CigarElement(length, operator))
519519
except KeyError as ex:
520520
# cigar operator was not valid
521-
raise cls._pretty_cigarstring_exception(cigarstring, i) from ex # type: ignore
521+
raise cls._pretty_cigarstring_exception(cigarstring, i) from ex
522522
except IndexError as ex:
523523
# missing cigar operator (i == len(cigarstring))
524-
raise cls._pretty_cigarstring_exception(cigarstring, i) from ex # type: ignore
524+
raise cls._pretty_cigarstring_exception(cigarstring, i) from ex
525525
i += 1
526526
return Cigar(tuple(elements))
527527

fgpyo/sam/builder.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from typing import List
2323
from typing import Optional
2424
from typing import Tuple
25+
from typing import cast
2526

2627
import pysam
2728
from pysam import AlignedSegment
@@ -154,7 +155,7 @@ def _next_name(self) -> str:
154155

155156
def _bases(self, length: int) -> str:
156157
"""Returns a random string of bases of the length requested."""
157-
return "".join(self._random.choices("ACGT", k=length)) # type: ignore
158+
return "".join(self._random.choices("ACGT", k=length))
158159

159160
def _new_rec(
160161
self,
@@ -334,13 +335,17 @@ def _set_mate_info(self, r1: pysam.AlignedSegment, r2: pysam.AlignedSegment) ->
334335

335336
def rg(self) -> Dict[str, Any]:
336337
"""Returns the single read group that is defined in the header."""
337-
rgs = self._header["RG"]
338+
# The `RG` field contains a list of read group mappings
339+
# e.g. `[{"ID": "rg1", "PL": "ILLUMINA"}]`
340+
rgs = cast(List[Dict[str, Any]], self._header["RG"])
338341
assert len(rgs) == 1, "Header did not contain exactly one read group!"
339342
return rgs[0]
340343

341344
def rg_id(self) -> str:
342345
"""Returns the ID of the single read group that is defined in the header."""
343-
return self.rg()["ID"]
346+
# The read group mapping has mixed types of values (e.g. "PI" is numeric), but the "ID"
347+
# field is always a string.
348+
return cast(str, self.rg()["ID"])
344349

345350
def add_pair(
346351
self,
@@ -585,7 +590,7 @@ def to_path(
585590
file_handle = fp.file
586591

587592
with sam.writer(
588-
file_handle, header=self._samheader, file_type=sam.SamFileType.BAM # type: ignore
593+
file_handle, header=self._samheader, file_type=sam.SamFileType.BAM
589594
) as writer:
590595
for rec in self._records:
591596
if pred(rec):

fgpyo/sam/tests/test_sam.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,11 @@ def test_pretty_cigarstring_exception() -> None:
227227
expected = "10M5[U]4M"
228228
with pytest.raises(CigarParsingException, match=r".*Malformed cigar") as ex:
229229
raise Cigar._pretty_cigarstring_exception(cigar, index)
230-
assert expected in str(ex)
230+
231+
# Known issue, `mypy` thinks the `raise` above makes the following unreachable
232+
# https://github.com/python/mypy/issues/8985
233+
# https://github.com/python/mypy/issues/8766
234+
assert expected in str(ex) # type: ignore[unreachable]
231235

232236
expected = cigar + "[]"
233237
with pytest.raises(CigarParsingException, match=r".*Malformed cigar") as ex:

fgpyo/util/inspect.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,14 @@
4444
from attr import fields as get_attr_fields
4545
from attr import fields_dict as get_attr_fields_dict
4646

47-
Attribute: TypeAlias = attr.Attribute # type: ignore[name-defined, no-redef]
47+
Attribute: TypeAlias = attr.Attribute # type: ignore[name-defined]
4848
# dataclasses and attr have internal tokens for missing values, join into a set so that we can
4949
# check if a value is missing without knowing the type of backing class
5050
MISSING = frozenset({DATACLASSES_MISSING, attr.NOTHING})
5151
except ImportError: # pragma: no cover
5252
_use_attr = False
5353
attr = None
54-
Attribute: TypeAlias = TypeVar("Attribute", bound=object) # type: ignore[misc, assignment, no-redef] # noqa: E501
54+
Attribute: TypeAlias = TypeVar("Attribute", bound=object) # type: ignore[misc, no-redef] # noqa: E501
5555

5656
# define empty placeholders for getting attr fields as a tuple or dict. They will never be
5757
# called because the import failed; but they're here to ensure that the function is defined in
@@ -85,7 +85,7 @@ class AttrsInstance(Protocol): # type: ignore[no-redef]
8585
__attrs_attrs__: ClassVar[Any]
8686

8787

88-
def is_attr_class(cls: type) -> bool: # type: ignore[arg-type]
88+
def is_attr_class(cls: type) -> bool:
8989
"""Return True if the class is an attr class, and False otherwise"""
9090
return hasattr(cls, "__attrs_attrs__")
9191

@@ -396,7 +396,7 @@ def get_fields_dict(
396396
) -> Mapping[str, FieldType]:
397397
"""Get the fields dict from either a dataclasses or attr dataclass (or instance)"""
398398
if is_dataclasses_class(cls):
399-
return _get_dataclasses_fields_dict(cls) # type: ignore[arg-type]
399+
return _get_dataclasses_fields_dict(cls)
400400
elif is_attr_class(cls): # type: ignore[arg-type]
401401
return get_attr_fields_dict(cls) # type: ignore[arg-type]
402402
else:
@@ -408,9 +408,9 @@ def get_fields(
408408
) -> Tuple[FieldType, ...]:
409409
"""Get the fields tuple from either a dataclasses or attr dataclass (or instance)"""
410410
if is_dataclasses_class(cls):
411-
return get_dataclasses_fields(cls) # type: ignore[arg-type]
411+
return get_dataclasses_fields(cls)
412412
elif is_attr_class(cls): # type: ignore[arg-type]
413-
return get_attr_fields(cls) # type: ignore[arg-type]
413+
return get_attr_fields(cls) # type: ignore[arg-type, no-any-return]
414414
else:
415415
raise TypeError("cls must a dataclasses or attr class")
416416

fgpyo/util/logging.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ def __init__(
105105
verb: str = "Read",
106106
unit: int = 100000,
107107
) -> None:
108+
self.printer: Callable[[str], Any]
108109
if isinstance(printer, Logger):
109110
self.printer = lambda s: printer.info(s)
110111
else:
@@ -187,6 +188,8 @@ def _log(
187188

188189
self.printer(f"{self.verb} {self.count:,d} {self.noun}: {coordinate}")
189190

191+
return None
192+
190193
def log_last(
191194
self,
192195
) -> bool:

fgpyo/util/types.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from typing import Type
1010
from typing import TypeVar
1111
from typing import Union
12+
from typing import cast
1213

1314
UnionType = TypeVar("UnionType", bound="Union")
1415
EnumType = TypeVar("EnumType", bound="Enum")
@@ -119,7 +120,9 @@ def _make_literal_parser_worker(
119120
for arg, p in zip(typing.get_args(literal), parsers):
120121
try:
121122
if p(value) == arg:
122-
return arg
123+
# typing.get_args returns `Any` because there's no guarantee on the input type, but
124+
# if we're passing it a literal then the returned args will always be `LiteralType`
125+
return cast(LiteralType, arg)
123126
except ValueError:
124127
pass
125128
raise InspectException(

0 commit comments

Comments
 (0)