Skip to content

Commit 111e15b

Browse files
authored
Drop support for python 3.7 (#98)
Drop support for python 3.7. Bumps version to `0.2.0-dev` since this is a potentially breaking change.
1 parent dbb600a commit 111e15b

File tree

9 files changed

+63
-200
lines changed

9 files changed

+63
-200
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"]
1313
steps:
1414
- uses: actions/checkout@v2
1515

.readthedocs.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ version: 2
22
build:
33
os: ubuntu-22.04
44
tools:
5-
python: "3.7"
5+
python: "3.8"
66
sphinx:
77
configuration: docs/conf.py
88
python:

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737

3838
`pip install fgpyo`
3939

40-
**Requires python 3.7+**
40+
**Requires python 3.8+**
4141

4242
See documentation on [fgpyo.readthedocs.org][rtd-link].
4343

@@ -48,7 +48,7 @@ See documentation on [fgpyo.readthedocs.org][rtd-link].
4848
A simple way to create an environment with the desired version of python and poetry is to use [conda][conda-link]. E.g.:
4949

5050
```bash
51-
conda create -n fgpyo -c conda-forge "python>=3.6" poetry
51+
conda create -n fgpyo -c conda-forge "python>=3.8" poetry
5252
conda activate fgpyo
5353
poetry install
5454
```

fgpyo/util/inspect.py

+22-25
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
@@ -112,8 +109,8 @@ def get_parser() -> partial:
112109
raise ValueError("Unable to parse set (try typing.Set[type])")
113110
elif type_ == dict:
114111
raise ValueError("Unable to parse dict (try typing.Mapping[type])")
115-
elif types.get_origin_type(type_) == list:
116-
subtypes = types.get_arg_types(type_)
112+
elif typing.get_origin(type_) == list:
113+
subtypes = typing.get_args(type_)
117114

118115
assert (
119116
len(subtypes) == 1
@@ -133,8 +130,8 @@ def get_parser() -> partial:
133130
]
134131
)
135132
)
136-
elif types.get_origin_type(type_) == set:
137-
subtypes = types.get_arg_types(type_)
133+
elif typing.get_origin(type_) == set:
134+
subtypes = typing.get_args(type_)
138135
assert (
139136
len(subtypes) == 1
140137
), "Sets are allowed only one subtype per PEP specification!"
@@ -153,14 +150,14 @@ def get_parser() -> partial:
153150
]
154151
)
155152
)
156-
elif types.get_origin_type(type_) == tuple:
153+
elif typing.get_origin(type_) == tuple:
157154
subtype_parsers = [
158155
_get_parser(
159156
cls,
160157
subtype,
161158
parsers,
162159
)
163-
for subtype in types.get_arg_types(type_)
160+
for subtype in typing.get_args(type_)
164161
]
165162

166163
def tuple_parse(tuple_string: str) -> Tuple[Any, ...]:
@@ -183,8 +180,8 @@ def tuple_parse(tuple_string: str) -> Tuple[Any, ...]:
183180

184181
return functools.partial(tuple_parse)
185182

186-
elif types.get_origin_type(type_) == dict:
187-
subtypes = types.get_arg_types(type_)
183+
elif typing.get_origin(type_) == dict:
184+
subtypes = typing.get_args(type_)
188185
assert (
189186
len(subtypes) == 2
190187
), "Dict object must have exactly 2 subtypes per PEP specification!"
@@ -231,15 +228,15 @@ def dict_parse(dict_string: str) -> Dict[Any, Any]:
231228
return functools.partial(type_)
232229
elif type_ == NoneType:
233230
return functools.partial(types.none_parser)
234-
elif types.get_origin_type(type_) is Union:
231+
elif typing.get_origin(type_) is Union:
235232
return types.make_union_parser(
236233
union=type_,
237-
parsers=[_get_parser(cls, arg, parsers) for arg in types.get_arg_types(type_)],
234+
parsers=[_get_parser(cls, arg, parsers) for arg in typing.get_args(type_)],
238235
)
239-
elif types.get_origin_type(type_) is Literal: # Py>=3.7.
236+
elif typing.get_origin(type_) is Literal:
240237
return types.make_literal_parser(
241238
type_,
242-
[_get_parser(cls, type(arg), parsers) for arg in types.get_arg_types(type_)],
239+
[_get_parser(cls, type(arg), parsers) for arg in typing.get_args(type_)],
243240
)
244241
else:
245242
raise ParserNotFoundException(
@@ -319,8 +316,8 @@ def attr_from(
319316

320317
def attribute_is_optional(attribute: attr.Attribute) -> bool:
321318
"""Returns True if the attribute is optional, False otherwise"""
322-
return types.get_origin_type(attribute.type) is Union and isinstance(
323-
None, types.get_arg_types(attribute.type)
319+
return typing.get_origin(attribute.type) is Union and isinstance(
320+
None, typing.get_args(attribute.type)
324321
)
325322

326323

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/tests/test_types.py

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from typing import Iterable
2+
from typing import List
3+
from typing import Sequence
4+
5+
from fgpyo.util import types
6+
7+
8+
def test_is_listlike() -> None:
9+
assert types.is_list_like(List[str])
10+
assert types.is_list_like(Iterable[str])
11+
assert types.is_list_like(Sequence[str])

fgpyo/util/types.py

+5-40
Original file line numberDiff line numberDiff line change
@@ -1,50 +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-
47-
4813
UnionType = TypeVar("UnionType", bound="Union")
4914
EnumType = TypeVar("EnumType", bound="Enum")
5015
# conceptually bound to "Literal" but that's not valid in the spec
@@ -109,7 +74,7 @@ def is_constructible_from_str(type_: type) -> bool:
10974

11075
def _is_optional(type_: type) -> bool:
11176
"""Returns true if type_ is optional"""
112-
return get_origin_type(type_) is Union and type(None) in get_arg_types(type_)
77+
return typing.get_origin(type_) is Union and type(None) in typing.get_args(type_)
11378

11479

11580
def _make_union_parser_worker(
@@ -150,15 +115,15 @@ def _make_literal_parser_worker(
150115
"""Worker function behind literal parsing. Iterates through possible literals and
151116
returns the value produced by the first literal that matches expectation.
152117
Otherwise raises an error if none work"""
153-
for arg, p in zip(get_arg_types(literal), parsers):
118+
for arg, p in zip(typing.get_args(literal), parsers):
154119
try:
155120
if p(value) == arg:
156121
return arg
157122
except ValueError:
158123
pass
159124
raise InspectException(
160125
"invalid choice: {!r} (choose from {})".format(
161-
value, ", ".join(map(repr, map(str, get_arg_types(literal))))
126+
value, ", ".join(map(repr, map(str, typing.get_args(literal))))
162127
)
163128
)
164129

@@ -174,7 +139,7 @@ def make_literal_parser(
174139

175140
def is_list_like(type_: type) -> bool:
176141
"""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]
142+
return typing.get_origin(type_) in [list, collections.abc.Iterable, collections.abc.Sequence]
178143

179144

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

0 commit comments

Comments
 (0)