Skip to content

Commit 86b5262

Browse files
authored
Merge pull request #357 from TotallyNotRobots/python3.9
feat: drop python 3.8 support
2 parents 8a6a442 + c0f3f63 commit 86b5262

15 files changed

Lines changed: 143 additions & 116 deletions

.devcontainer/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM mcr.microsoft.com/devcontainers/python:3.8@sha256:13822a0e211e5b99816ce3f44f064ee385f7679eb407f901f19ed5328ad557d0
1+
FROM mcr.microsoft.com/devcontainers/python:3.12@sha256:7876580dc67fd460fd962f004cbeb480027e9bbc0657096f1087db11f9eaff39
22

33
RUN \
44
pipx uninstall mypy \

.github/workflows/python-tests.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ jobs:
2121
fail-fast: false
2222
matrix:
2323
python-version:
24-
- '3.8'
2524
- '3.9'
2625
- '3.10'
2726
- '3.11'
@@ -47,4 +46,4 @@ jobs:
4746
SKIP: nitpick
4847

4948
- name: Run tests
50-
run: hatch test --python ${{ matrix.python-version }} --cover --randomize --parallel --retries 2 --retry-delay 1
49+
run: hatch test --python ${{ matrix.python-version }} --cover --randomize --retries 2 --retry-delay 1

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ repos:
5858
hooks:
5959
- id: pyupgrade
6060
args:
61-
- --py38-plus
61+
- --py39-plus
6262
- repo: https://github.com/MarcoGorelli/auto-walrus
6363
rev: 7855759486496a3248e9ff37dce7c6d57d39bfce
6464
hooks:

irclib/parser.py

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@
55

66
import re
77
from abc import ABCMeta, abstractmethod
8+
from collections.abc import Iterable, Iterator, Sequence
89
from typing import (
910
Dict,
1011
Final,
11-
Iterable,
12-
Iterator,
1312
List,
13+
Literal,
1414
Optional,
15-
Sequence,
1615
Tuple,
1716
TypeVar,
1817
Union,
@@ -34,7 +33,7 @@
3433
)
3534
MsgTagList: TypeAlias = Optional["TagList"]
3635
MsgPrefix: TypeAlias = Optional["Prefix"]
37-
MessageTuple: TypeAlias = Tuple[MsgTagList, MsgPrefix, str, "ParamList"]
36+
MessageTuple: TypeAlias = tuple[MsgTagList, MsgPrefix, str, "ParamList"]
3837

3938
TAGS_SENTINEL: Final = "@"
4039
TAGS_SEP: Final = ";"
@@ -106,7 +105,7 @@ def value(self) -> Optional[str]:
106105
"""CAP value."""
107106
return self._value
108107

109-
def as_tuple(self) -> Tuple[str, Optional[str]]:
108+
def as_tuple(self) -> tuple[str, Optional[str]]:
110109
"""Get data as a tuple of values."""
111110
return self.name, self.value
112111

@@ -144,7 +143,7 @@ def __str__(self) -> str:
144143
return self.name
145144

146145

147-
class CapList(Parseable, List[Cap]):
146+
class CapList(Parseable, list[Cap]):
148147
"""Represents a list of CAP entities."""
149148

150149
@classmethod
@@ -293,7 +292,7 @@ def __str__(self) -> str:
293292
return self.name
294293

295294

296-
class TagList(Parseable, Dict[str, MessageTag]):
295+
class TagList(Parseable, dict[str, MessageTag]):
297296
"""Object representing the list of message tags on a line."""
298297

299298
def __init__(self, tags: Iterable[MessageTag] = ()) -> None:
@@ -305,23 +304,27 @@ def __init__(self, tags: Iterable[MessageTag] = ()) -> None:
305304
super().__init__((tag.name, tag) for tag in tags)
306305

307306
@staticmethod
308-
def _cmp_type_map(obj: object) -> Dict[str, MessageTag]:
307+
def _cmp_type_map(
308+
obj: object,
309+
) -> Union[
310+
tuple[dict[str, MessageTag], Literal[True]], tuple[None, Literal[False]]
311+
]:
309312
if isinstance(obj, str):
310-
return TagList.parse(obj)
313+
return TagList.parse(obj), True
311314

312315
if isinstance(obj, dict):
313316
sample = next(iter(obj.values()), None)
314317
if obj and (sample is None or isinstance(sample, str)):
315318
# Handle str -> str dict
316-
return TagList.from_dict(obj)
319+
return TagList.from_dict(obj), True
317320

318321
# Handle str -> MessageTag dict
319-
return dict(obj)
322+
return dict(obj), True
320323

321324
if isinstance(obj, list):
322-
return TagList(obj)
325+
return TagList(obj), True
323326

324-
return NotImplemented
327+
return None, False
325328

326329
@classmethod
327330
def parse(cls, text: str) -> Self:
@@ -333,25 +336,25 @@ def parse(cls, text: str) -> Self:
333336
return cls(map(MessageTag.parse, filter(None, text.split(TAGS_SEP))))
334337

335338
@classmethod
336-
def from_dict(cls, tags: Dict[str, str]) -> Self:
339+
def from_dict(cls, tags: dict[str, str]) -> Self:
337340
"""Create a TagList from a dict of tags."""
338341
return cls(MessageTag(k, v) for k, v in tags.items())
339342

340343
def __eq__(self, other: object) -> bool:
341344
"""Compare to another tag list, string, or list of MessageTag objects."""
342-
obj = self._cmp_type_map(other)
343-
if obj is NotImplemented:
345+
res = self._cmp_type_map(other)
346+
if not res[1]:
344347
return NotImplemented
345348

346-
return dict(self) == dict(obj)
349+
return dict(self) == dict(res[0])
347350

348351
def __ne__(self, other: object) -> bool:
349352
"""Compare to another tag list, string, or list of MessageTag objects."""
350-
obj = self._cmp_type_map(other)
351-
if obj is NotImplemented:
353+
res = self._cmp_type_map(other)
354+
if not res[1]:
352355
return NotImplemented
353356

354-
return dict(self) != dict(obj)
357+
return dict(self) != dict(res[0])
355358

356359
def __str__(self) -> str:
357360
"""Represent the tag list as a string."""
@@ -405,7 +408,7 @@ def mask(self) -> str:
405408
return mask
406409

407410
@property
408-
def _data(self) -> Tuple[str, str, str]:
411+
def _data(self) -> tuple[str, str, str]:
409412
return self.nick, self.user, self.host
410413

411414
@classmethod
@@ -463,7 +466,7 @@ def __str__(self) -> str:
463466
return self.mask
464467

465468

466-
class ParamList(Parseable, List[str]):
469+
class ParamList(Parseable, list[str]):
467470
"""An object representing the parameter list from a line."""
468471

469472
def __init__(self, *params: str, has_trail: bool = False) -> None:
@@ -554,13 +557,13 @@ def __str__(self) -> str:
554557

555558

556559
def _parse_tags(
557-
tags: Union[TagList, Dict[str, str], str, None, List[str]],
560+
tags: Union[TagList, dict[str, str], str, None, list[str]],
558561
) -> MsgTagList:
559562
if isinstance(tags, TagList):
560563
return tags
561564

562565
if isinstance(tags, dict):
563-
return TagList.from_dict(cast(Dict[str, str], tags))
566+
return TagList.from_dict(cast(dict[str, str], tags))
564567

565568
if isinstance(tags, str):
566569
return TagList.parse(tags)
@@ -585,7 +588,7 @@ def _parse_prefix(prefix: Union[Prefix, str, None, Iterable[str]]) -> MsgPrefix:
585588

586589

587590
def _parse_params(
588-
parameters: Tuple[Union[str, List[str], ParamList], ...],
591+
parameters: tuple[Union[str, list[str], ParamList], ...],
589592
) -> ParamList:
590593
if len(parameters) == 1 and not isinstance(parameters[0], str):
591594
# This seems to be a list
@@ -594,18 +597,18 @@ def _parse_params(
594597

595598
return ParamList.from_list(parameters[0])
596599

597-
return ParamList.from_list(cast(Tuple[str, ...], parameters))
600+
return ParamList.from_list(cast(tuple[str, ...], parameters))
598601

599602

600603
class Message(Parseable):
601604
"""An object representing a parsed IRC line."""
602605

603606
def __init__(
604607
self,
605-
tags: Union[TagList, Dict[str, str], str, None, List[str]],
608+
tags: Union[TagList, dict[str, str], str, None, list[str]],
606609
prefix: Union[str, Prefix, None, Iterable[str]],
607610
command: str,
608-
*parameters: Union[str, List[str], ParamList],
611+
*parameters: Union[str, list[str], ParamList],
609612
) -> None:
610613
"""Construct message object."""
611614
self._tags = _parse_tags(tags)

irclib/util/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
"""IRC utils."""
22

3+
from irclib.util import commands, compare, frozendict, numerics, string
4+
35
__all__ = ("commands", "compare", "frozendict", "numerics", "string")

irclib/util/commands.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""IRC command data and utilities."""
22

3-
from typing import Iterator, List, Mapping, Optional, cast
3+
from collections.abc import Iterator, Mapping
4+
from typing import List, Optional, cast
45

56
import attr
67

@@ -44,7 +45,7 @@ class Command:
4445
"""A single IRC command."""
4546

4647
name: str
47-
args: List[CommandArgument]
48+
args: list[CommandArgument]
4849
min_args: int = 0
4950
max_args: Optional[int] = None
5051

irclib/util/frozendict.py

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,7 @@
11
"""Frozen Dict."""
22

3-
from typing import (
4-
Dict,
5-
Iterable,
6-
Iterator,
7-
Mapping,
8-
Optional,
9-
Tuple,
10-
TypeVar,
11-
Union,
12-
)
3+
from collections.abc import Iterable, Iterator, Mapping
4+
from typing import Dict, Optional, Tuple, TypeVar, Union
135

146
from typing_extensions import Self
157

@@ -28,13 +20,13 @@ class FrozenDict(Mapping[str, _V]):
2820

2921
def __init__(
3022
self,
31-
seq: Union[Mapping[str, _V], Iterable[Tuple[str, _V]], None] = None,
23+
seq: Union[Mapping[str, _V], Iterable[tuple[str, _V]], None] = None,
3224
**kwargs: _V,
3325
) -> None:
3426
"""Construct a FrozenDict."""
3527
d = dict(seq, **kwargs) if seq is not None else dict(**kwargs)
3628

37-
self.__data: Dict[str, _V] = d
29+
self.__data: dict[str, _V] = d
3830
self.__hash: Optional[int] = None
3931

4032
def copy(self, **kwargs: _V) -> Self:

irclib/util/numerics.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""IRC numeric mapping."""
22

3+
from collections.abc import Iterator, Mapping
34
from dataclasses import dataclass
4-
from typing import Iterator, Mapping
55

66
__all__ = ("Numeric", "numerics")
77

0 commit comments

Comments
 (0)