From b88774a871ed82d62e6ac91d38cbbd288d7a8bff Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Thu, 30 Mar 2023 15:24:09 -0400 Subject: [PATCH 1/6] overlapping_ranges: fix type annotations. This function takes as input a pair of *sequences* of 2-tuples (not a pair of 2-tuples) and returns a list of 2-tuples. --- wfdb/io/util.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/wfdb/io/util.py b/wfdb/io/util.py index 12ecde33..1b3f4ad9 100644 --- a/wfdb/io/util.py +++ b/wfdb/io/util.py @@ -4,7 +4,7 @@ import math import os -from typing import Sequence, Tuple +from typing import List, Sequence, Tuple def lines_to_file(file_name: str, write_dir: str, lines: Sequence[str]): @@ -102,8 +102,9 @@ def upround(x, base): def overlapping_ranges( - ranges_1: Tuple[int, int], ranges_2: Tuple[int, int] -) -> Tuple[int, int]: + ranges_1: Sequence[Tuple[int, int]], + ranges_2: Sequence[Tuple[int, int]], +) -> List[Tuple[int, int]]: """ Given two collections of integer ranges, return a list of ranges in which both input inputs overlap. From 83cdfaa795d39f7458385d07ce7ede55966afe78 Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Thu, 30 Mar 2023 15:36:19 -0400 Subject: [PATCH 2/6] Fix type inference for ALLOWED_TYPES. When using mypy to check the package, it will attempt to infer types that are not specified. Currently, mypy is able to understand that constructing a dict from a Sequence[Tuple[X, Y]] yields a Dict[X, Y], but if we use lists instead of tuples, mypy doesn't understand and seems to think the result is a Dict[X, X]. This is probably a bug in mypy, but in any case, using tuples here rather than lists is more idiomatic and doesn't affect the behavior of the code. --- wfdb/io/record.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wfdb/io/record.py b/wfdb/io/record.py index 860c908d..94cae73b 100644 --- a/wfdb/io/record.py +++ b/wfdb/io/record.py @@ -1661,7 +1661,7 @@ def multi_to_single(self, physical, return_res=64, expanded=False): # this library ALLOWED_TYPES = dict( [ - [index, _header.FIELD_SPECS.loc[index, "allowed_types"]] + (index, _header.FIELD_SPECS.loc[index, "allowed_types"]) for index in _header.FIELD_SPECS.index ] ) From 5c00559c2d87e424a057f36d536e1ba92f25e569 Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Thu, 30 Mar 2023 15:44:37 -0400 Subject: [PATCH 3/6] Config: declare type of the db_index_url attribute. mypy (and likely other static analysis tools) will complain if an object attribute is used without being defined either in the class or the class's __init__ method. Declare the attribute here so that mypy knows it exists. --- wfdb/io/download.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wfdb/io/download.py b/wfdb/io/download.py index ace112f9..d494ad0e 100644 --- a/wfdb/io/download.py +++ b/wfdb/io/download.py @@ -23,7 +23,7 @@ class Config(object): """ - pass + db_index_url: str # The configuration database index url. Uses PhysioNet index by default. From 21e8674e6622d81dd8db55cf10af2f65d88bf592 Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Thu, 30 Mar 2023 15:50:59 -0400 Subject: [PATCH 4/6] contained_combined_ranges: fix type annotation. This function requires as input a Sequence of signal names, not merely a Collection. (There's no particular reason this function *couldn't* be written to accept any Collection or even any Iterable, but at present it requires the argument to be something that implements both __len__ and __getitem__.) --- wfdb/io/_header.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wfdb/io/_header.py b/wfdb/io/_header.py index a5dc2872..6c811299 100644 --- a/wfdb/io/_header.py +++ b/wfdb/io/_header.py @@ -1,5 +1,5 @@ import datetime -from typing import Collection, List, Tuple +from typing import List, Sequence, Tuple import numpy as np import pandas as pd @@ -920,7 +920,7 @@ def contained_ranges(self, sig_name: str) -> List[Tuple[int, int]]: def contained_combined_ranges( self, - sig_names: Collection[str], + sig_names: Sequence[str], ) -> List[Tuple[int, int]]: """ Given a collection of signal name, return the sample ranges that From b24e469e6767ec5fce2ced27680d248b1b322674 Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Thu, 30 Mar 2023 15:58:28 -0400 Subject: [PATCH 5/6] MultiHeaderMixin: declare types of object attributes. mypy (and likely other static analysis tools) will complain if an object attribute is used without being defined either in the class or the class's __init__ method. The MultiHeaderMixin class is not public and not meant to be instantiated at all, but refers to attributes that are defined by the MultiRecord class which inherits from it. Declare the types of these attributes here so that mypy knows they exist. --- wfdb/io/_header.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/wfdb/io/_header.py b/wfdb/io/_header.py index 6c811299..2fe16425 100644 --- a/wfdb/io/_header.py +++ b/wfdb/io/_header.py @@ -1,5 +1,5 @@ import datetime -from typing import List, Sequence, Tuple +from typing import List, Optional, Sequence, Tuple import numpy as np import pandas as pd @@ -598,6 +598,10 @@ class MultiHeaderMixin(BaseHeaderMixin): """ + n_seg: int + seg_len: Sequence[int] + segments: Optional[Sequence] + def set_defaults(self): """ Set defaults for fields needed to write the header if they have From 5564dc93961ad51580f4972aec428fe4969f3273 Mon Sep 17 00:00:00 2001 From: Benjamin Moody Date: Thu, 30 Mar 2023 16:09:50 -0400 Subject: [PATCH 6/6] _parse_record_line: annotate type of record_fields variable. This variable is used in a strange way; initially it contains string values extracted from the record line, then later these string values are replaced with appropriately-typed values for each field. However, there's no real way to make this type-safe without changing the API of the function. Annotate the variable's type so that mypy will not treat it as a Dict[str, Optional[str]]. --- wfdb/io/_header.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wfdb/io/_header.py b/wfdb/io/_header.py index 2fe16425..a1feb5f2 100644 --- a/wfdb/io/_header.py +++ b/wfdb/io/_header.py @@ -1,5 +1,5 @@ import datetime -from typing import List, Optional, Sequence, Tuple +from typing import Any, Dict, List, Optional, Sequence, Tuple import numpy as np import pandas as pd @@ -1014,7 +1014,7 @@ def _parse_record_line(record_line: str) -> dict: """ # Dictionary for record fields - record_fields = {} + record_fields: Dict[str, Any] = {} # Read string fields from record line match = rx_record.match(record_line)