Skip to content

Commit 6326d84

Browse files
committed
drop py37 support, remove dependencies related to typing backward-compatibility
1 parent 94b3696 commit 6326d84

13 files changed

+523
-676
lines changed

.github/workflows/pythonpackage.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
runs-on: ubuntu-latest
1010
strategy:
1111
matrix:
12-
PYTHON_VERSION: ["3.7", "3.8", "3.9", "3.10"]
12+
PYTHON_VERSION: ["3.8", "3.9", "3.10", "3.11", "3.12"]
1313
steps:
1414
- uses: actions/checkout@v2
1515

fgpyo/fasta/builder.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
'AAAAAAAAAANNN'
3333
3434
"""
35+
3536
import textwrap
3637
from pathlib import Path
3738
from typing import TYPE_CHECKING
@@ -48,7 +49,6 @@ def samtools_dict(*args: Any) -> None:
4849
def samtools_faidx(*args: Any) -> None:
4950
pass
5051

51-
5252
else:
5353
from pysam import dict as samtools_dict
5454
from pysam import faidx as samtools_faidx

fgpyo/fastx/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
seq2: GGGG, seq2: TTTT
2525
2626
"""
27+
2728
from contextlib import AbstractContextManager
2829
from pathlib import Path
2930
from types import TracebackType

fgpyo/io/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"10"
4343
4444
"""
45+
4546
import gzip
4647
import io
4748
import os

fgpyo/read_structure.py

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
correspond to the given read segment
6262
6363
"""
64+
6465
import enum
6566
from typing import Iterable
6667
from typing import Iterator

fgpyo/sam/builder.py

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- :class:`~fgpyo.sam.builder.SamBuilder` -- A builder class that allows the accumulation
1111
of alignment records and access as a list and writing to file.
1212
"""
13+
1314
from array import array
1415
from pathlib import Path
1516
from random import Random

fgpyo/util/inspect.py

+57-34
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,25 @@
1+
import functools
12
import sys
3+
import typing
4+
from enum import Enum
5+
from functools import partial
6+
from pathlib import PurePath
27
from typing import Any
8+
from typing import Callable
39
from typing import Dict
410
from typing import Iterable
511
from typing import List
12+
from typing import Literal
13+
from typing import Optional
614
from typing import Tuple
715
from typing import Type
816
from typing import Union
917

10-
if sys.version_info >= (3, 8):
11-
from typing import Literal
12-
else:
13-
from typing_extensions import Literal
1418
if sys.version_info >= (3, 12):
1519
from typing import TypeAlias
1620
else:
1721
from typing_extensions import TypeAlias
1822

19-
import functools
20-
from enum import Enum
21-
from functools import partial
22-
from pathlib import PurePath
23-
from typing import Callable
24-
from typing import Optional
25-
2623
import attr
2724

2825
import fgpyo.util.types as types
@@ -60,18 +57,24 @@ def split_at_given_level(
6057
decrease_in_depth += high_level_split.count(char)
6158
outer_depth_of_split += increase_in_depth - decrease_in_depth
6259

63-
assert outer_depth_of_split >= 0, "Unpaired depth character! Likely incorrect output"
60+
assert (
61+
outer_depth_of_split >= 0
62+
), "Unpaired depth character! Likely incorrect output"
6463

6564
current_outer_splits.append(high_level_split)
6665
if outer_depth_of_split == 0:
6766
out_vals.append(split_delim.join(current_outer_splits))
6867
current_outer_splits = []
69-
assert outer_depth_of_split == 0, "Unpaired depth character! Likely incorrect output!"
68+
assert (
69+
outer_depth_of_split == 0
70+
), "Unpaired depth character! Likely incorrect output!"
7071
return out_vals
7172

7273

7374
def _get_parser(
74-
cls: Type, type_: TypeAlias, parsers: Optional[Dict[type, Callable[[str], Any]]] = None
75+
cls: Type,
76+
type_: TypeAlias,
77+
parsers: Optional[Dict[type, Callable[[str], Any]]] = None,
7578
) -> partial:
7679
"""Attempts to find a parser for a provided type.
7780
@@ -109,8 +112,8 @@ def get_parser() -> partial:
109112
raise ValueError("Unable to parse set (try typing.Set[type])")
110113
elif type_ == dict:
111114
raise ValueError("Unable to parse dict (try typing.Mapping[type])")
112-
elif types.get_origin_type(type_) == list:
113-
subtypes = types.get_arg_types(type_)
115+
elif typing.get_origin(type_) == list:
116+
subtypes = typing.get_args(type_)
114117

115118
assert (
116119
len(subtypes) == 1
@@ -130,8 +133,8 @@ def get_parser() -> partial:
130133
]
131134
)
132135
)
133-
elif types.get_origin_type(type_) == set:
134-
subtypes = types.get_arg_types(type_)
136+
elif typing.get_origin(type_) == set:
137+
subtypes = typing.get_args(type_)
135138
assert (
136139
len(subtypes) == 1
137140
), "Sets are allowed only one subtype per PEP specification!"
@@ -146,18 +149,20 @@ def get_parser() -> partial:
146149
if s == "{}"
147150
else [
148151
subtype_parser(item)
149-
for item in set(split_at_given_level(s[1:-1], split_delim=","))
152+
for item in set(
153+
split_at_given_level(s[1:-1], split_delim=",")
154+
)
150155
]
151156
)
152157
)
153-
elif types.get_origin_type(type_) == tuple:
158+
elif typing.get_origin(type_) == tuple:
154159
subtype_parsers = [
155160
_get_parser(
156161
cls,
157162
subtype,
158163
parsers,
159164
)
160-
for subtype in types.get_arg_types(type_)
165+
for subtype in typing.get_args(type_)
161166
]
162167

163168
def tuple_parse(tuple_string: str) -> Tuple[Any, ...]:
@@ -172,16 +177,18 @@ def tuple_parse(tuple_string: str) -> Tuple[Any, ...]:
172177
if len(tuple_string) == 0:
173178
return ()
174179
else:
175-
val_strings = split_at_given_level(tuple_string, split_delim=",")
180+
val_strings = split_at_given_level(
181+
tuple_string, split_delim=","
182+
)
176183
return tuple(
177184
parser(val_str)
178185
for parser, val_str in zip(subtype_parsers, val_strings)
179186
)
180187

181188
return functools.partial(tuple_parse)
182189

183-
elif types.get_origin_type(type_) == dict:
184-
subtypes = types.get_arg_types(type_)
190+
elif typing.get_origin(type_) == dict:
191+
subtypes = typing.get_args(type_)
185192
assert (
186193
len(subtypes) == 2
187194
), "Dict object must have exactly 2 subtypes per PEP specification!"
@@ -208,10 +215,14 @@ def dict_parse(dict_string: str) -> Dict[Any, Any]:
208215
if len(dict_string) == 0:
209216
return {}
210217
else:
211-
outer_splits = split_at_given_level(dict_string, split_delim=",")
218+
outer_splits = split_at_given_level(
219+
dict_string, split_delim=","
220+
)
212221
out_dict = {}
213222
for outer_split in outer_splits:
214-
inner_splits = split_at_given_level(outer_split, split_delim=";")
223+
inner_splits = split_at_given_level(
224+
outer_split, split_delim=";"
225+
)
215226
assert (
216227
len(inner_splits) % 2 == 0
217228
), "Inner splits of dict didn't have matched key val pairs"
@@ -228,15 +239,21 @@ def dict_parse(dict_string: str) -> Dict[Any, Any]:
228239
return functools.partial(type_)
229240
elif isinstance(type_, type(type(None))):
230241
return functools.partial(types.none_parser)
231-
elif types.get_origin_type(type_) is Union:
242+
elif typing.get_origin(type_) is Union:
232243
return types.make_union_parser(
233244
union=type_,
234-
parsers=[_get_parser(cls, arg, parsers) for arg in types.get_arg_types(type_)],
245+
parsers=[
246+
_get_parser(cls, arg, parsers)
247+
for arg in typing.get_args(type_)
248+
],
235249
)
236-
elif types.get_origin_type(type_) is Literal: # Py>=3.7.
250+
elif typing.get_origin(type_) is Literal: # Py>=3.7.
237251
return types.make_literal_parser(
238252
type_,
239-
[_get_parser(cls, type(arg), parsers) for arg in types.get_arg_types(type_)],
253+
[
254+
_get_parser(cls, type(arg), parsers)
255+
for arg in typing.get_args(type_)
256+
],
240257
)
241258
else:
242259
raise ParserNotFoundException(
@@ -255,7 +272,9 @@ def dict_parse(dict_string: str) -> Dict[Any, Any]:
255272

256273

257274
def attr_from(
258-
cls: Type, kwargs: Dict[str, str], parsers: Optional[Dict[type, Callable[[str], Any]]] = None
275+
cls: Type,
276+
kwargs: Dict[str, str],
277+
parsers: Optional[Dict[type, Callable[[str], Any]]] = None,
259278
) -> Any:
260279
"""Builds an attr class from key-word arguments
261280
@@ -289,7 +308,11 @@ def attr_from(
289308
# try setting by casting
290309
# Note that while bools *can* be cast from string, all non-empty strings evaluate to
291310
# True, because python, so we need to check for that explicitly
292-
if not set_value and attribute.type is not None and not attribute.type == bool:
311+
if (
312+
not set_value
313+
and attribute.type is not None
314+
and not attribute.type == bool
315+
):
293316
try:
294317
return_value = attribute.type(str_value)
295318
set_value = True
@@ -316,8 +339,8 @@ def attr_from(
316339

317340
def attribute_is_optional(attribute: attr.Attribute) -> bool:
318341
"""Returns True if the attribute is optional, False otherwise"""
319-
return types.get_origin_type(attribute.type) is Union and isinstance(
320-
None, types.get_arg_types(attribute.type)
342+
return typing.get_origin(attribute.type) is Union and isinstance(
343+
None, typing.get_args(attribute.type)
321344
)
322345

323346

fgpyo/util/logging.py

+1-7
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,13 @@
3434
"""
3535

3636
import logging
37-
import sys
38-
39-
if sys.version_info >= (3, 8):
40-
from typing import Literal
41-
else:
42-
from typing_extensions import Literal
43-
4437
import socket
4538
from contextlib import AbstractContextManager
4639
from logging import Logger
4740
from threading import RLock
4841
from typing import Any
4942
from typing import Callable
43+
from typing import Literal
5044
from typing import Optional
5145
from typing import Union
5246

fgpyo/util/types.py

+15-41
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,15 @@
11
import collections
22
import inspect
3-
import sys
43
import typing
54
from enum import Enum
65
from functools import partial
76
from typing import Callable
87
from typing import Iterable
8+
from typing import Literal
99
from typing import Type
1010
from typing import TypeVar
1111
from typing import Union
1212

13-
# `get_origin_type` is a method that gets the outer type (ex list in a List[str])
14-
# `get_arg_types` is a method that gets the inner type (ex str in a List[str])
15-
if sys.version_info >= (3, 8):
16-
from typing import Literal
17-
18-
get_origin_type = typing.get_origin
19-
get_arg_types = typing.get_args
20-
else:
21-
import typing_inspect
22-
from typing_extensions import Literal
23-
24-
def get_origin_type(tp: Type) -> Type:
25-
"""Returns the outer type of a Typing object (ex list in a List[T])"""
26-
27-
if type(tp) is type(Literal): # Py<=3.6.
28-
return Literal
29-
origin = typing_inspect.get_origin(tp)
30-
return {
31-
typing.List: list,
32-
typing.Iterable: collections.abc.Iterable,
33-
typing.Sequence: collections.abc.Sequence,
34-
typing.Tuple: tuple,
35-
typing.Set: set,
36-
typing.Mapping: dict,
37-
typing.Dict: dict,
38-
}.get(origin, origin)
39-
40-
def get_arg_types(tp: Type) -> Type:
41-
"""Gets the inner types of a Typing object (ex T in a List[T])"""
42-
43-
if type(tp) is type(Literal): # Py<=3.6.
44-
return tp.__values__
45-
return typing_inspect.get_args(tp, evaluate=True) # evaluate=True default on Py>=3.7.
46-
4713

4814
UnionType = TypeVar("UnionType", bound="Union")
4915
EnumType = TypeVar("EnumType", bound="Enum")
@@ -109,7 +75,7 @@ def is_constructible_from_str(type_: type) -> bool:
10975

11076
def _is_optional(type_: type) -> bool:
11177
"""Returns true if type_ is optional"""
112-
return get_origin_type(type_) is Union and type(None) in get_arg_types(type_)
78+
return typing.get_origin(type_) is Union and type(None) in typing.get_args(type_)
11379

11480

11581
def _make_union_parser_worker(
@@ -137,28 +103,32 @@ def _make_union_parser_worker(
137103
raise ValueError(f"{value} could not be parsed as any of {union}")
138104

139105

140-
def make_union_parser(union: Type[UnionType], parsers: Iterable[Callable[[str], type]]) -> partial:
106+
def make_union_parser(
107+
union: Type[UnionType], parsers: Iterable[Callable[[str], type]]
108+
) -> partial:
141109
"""Generates a parser function for a union type object and set of parsers for the possible
142110
parsers to that union type object
143111
"""
144112
return partial(_make_union_parser_worker, union, parsers)
145113

146114

147115
def _make_literal_parser_worker(
148-
literal: Type[LiteralType], parsers: Iterable[Callable[[str], LiteralType]], value: str
116+
literal: Type[LiteralType],
117+
parsers: Iterable[Callable[[str], LiteralType]],
118+
value: str,
149119
) -> LiteralType:
150120
"""Worker function behind literal parsing. Iterates through possible literals and
151121
returns the value produced by the first literal that matches expectation.
152122
Otherwise raises an error if none work"""
153-
for arg, p in zip(get_arg_types(literal), parsers):
123+
for arg, p in zip(typing.get_args(literal), parsers):
154124
try:
155125
if p(value) == arg:
156126
return arg
157127
except ValueError:
158128
pass
159129
raise InspectException(
160130
"invalid choice: {!r} (choose from {})".format(
161-
value, ", ".join(map(repr, map(str, get_arg_types(literal))))
131+
value, ", ".join(map(repr, map(str, typing.get_args(literal))))
162132
)
163133
)
164134

@@ -174,7 +144,11 @@ def make_literal_parser(
174144

175145
def is_list_like(type_: type) -> bool:
176146
"""Returns true if the value is a list or list like object"""
177-
return get_origin_type(type_) in [list, collections.abc.Iterable, collections.abc.Sequence]
147+
return typing.get_origin(type_) in [
148+
list,
149+
collections.abc.Iterable,
150+
collections.abc.Sequence,
151+
]
178152

179153

180154
def none_parser(value: str) -> Literal[None]:

0 commit comments

Comments
 (0)