Skip to content

Commit 1582ba5

Browse files
authored
build: Bump minimum Python version to 3.9 (fixes #585) (#610)
* Update license metadata. Avoid deprecation warnings with modern setuptools, which will stop supporting a table as the project.license option in pyproject.toml soon. Require a modern version >= 77 to make use of the alternative options. * Modernize Dict and List type annotations. Since Python 3.9 we can use the built-in types directly. * Simplify type import. * Modernize Iterator type annotations. Since Python 3.9 the collections.abc module can be used directly for typing.
1 parent 317fd3d commit 1582ba5

File tree

10 files changed

+39
-39
lines changed

10 files changed

+39
-39
lines changed

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ The aim of the project is to support the most common parts of the CiA 301
66
standard in a simple Pythonic interface. It is mainly targeted for testing and
77
automation tasks rather than a standard compliant master implementation.
88

9-
The library supports Python 3.8 or newer.
9+
The library supports Python 3.9 or newer.
1010

1111

1212
Features

canopen/emcy.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
from __future__ import annotations
2+
13
import logging
24
import struct
35
import threading
46
import time
5-
from typing import Callable, List, Optional
7+
from typing import Callable, Optional
68

79
import canopen.network
810

@@ -17,9 +19,9 @@ class EmcyConsumer:
1719

1820
def __init__(self):
1921
#: Log of all received EMCYs for this node
20-
self.log: List["EmcyError"] = []
22+
self.log: list[EmcyError] = []
2123
#: Only active EMCYs. Will be cleared on Error Reset
22-
self.active: List["EmcyError"] = []
24+
self.active: list[EmcyError] = []
2325
self.callbacks = []
2426
self.emcy_received = threading.Condition()
2527

@@ -39,7 +41,7 @@ def on_emcy(self, can_id, data, timestamp):
3941
for callback in self.callbacks:
4042
callback(entry)
4143

42-
def add_callback(self, callback: Callable[["EmcyError"], None]):
44+
def add_callback(self, callback: Callable[[EmcyError], None]):
4345
"""Get notified on EMCY messages from this node.
4446
4547
:param callback:
@@ -55,7 +57,7 @@ def reset(self):
5557

5658
def wait(
5759
self, emcy_code: Optional[int] = None, timeout: float = 10
58-
) -> "EmcyError":
60+
) -> EmcyError:
5961
"""Wait for a new EMCY to arrive.
6062
6163
:param emcy_code: EMCY code to wait for

canopen/network.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22

33
import logging
44
import threading
5-
from collections.abc import MutableMapping
6-
from typing import Callable, Dict, Final, Iterator, List, Optional, Union
5+
from collections.abc import Iterator, MutableMapping
6+
from typing import Callable, Final, Optional, Union
77

88
import can
9-
from can import Listener
109

1110
from canopen.lss import LssMaster
1211
from canopen.nmt import NmtMaster
@@ -40,10 +39,10 @@ def __init__(self, bus: Optional[can.BusABC] = None):
4039
self.scanner = NodeScanner(self)
4140
#: List of :class:`can.Listener` objects.
4241
#: Includes at least MessageListener.
43-
self.listeners = [MessageListener(self)]
42+
self.listeners: list[can.Listener] = [MessageListener(self)]
4443
self.notifier: Optional[can.Notifier] = None
45-
self.nodes: Dict[int, Union[RemoteNode, LocalNode]] = {}
46-
self.subscribers: Dict[int, List[Callback]] = {}
44+
self.nodes: dict[int, Union[RemoteNode, LocalNode]] = {}
45+
self.subscribers: dict[int, list[Callback]] = {}
4746
self.send_lock = threading.Lock()
4847
self.sync = SyncProducer(self)
4948
self.time = TimeProducer(self)
@@ -352,7 +351,7 @@ def update(self, data: bytes) -> None:
352351
self._start()
353352

354353

355-
class MessageListener(Listener):
354+
class MessageListener(can.Listener):
356355
"""Listens for messages on CAN bus and feeds them to a Network instance.
357356
358357
:param network:
@@ -396,7 +395,7 @@ def __init__(self, network: Optional[Network] = None):
396395
network = _UNINITIALIZED_NETWORK
397396
self.network: Network = network
398397
#: A :class:`list` of nodes discovered
399-
self.nodes: List[int] = []
398+
self.nodes: list[int] = []
400399

401400
def on_message_received(self, can_id: int):
402401
service = can_id & 0x780

canopen/nmt.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import struct
33
import threading
44
import time
5-
from typing import Callable, Dict, Final, List, Optional, TYPE_CHECKING
5+
from typing import Callable, Final, Optional, TYPE_CHECKING
66

77
import canopen.network
88

@@ -12,7 +12,7 @@
1212

1313
logger = logging.getLogger(__name__)
1414

15-
NMT_STATES: Final[Dict[int, str]] = {
15+
NMT_STATES: Final[dict[int, str]] = {
1616
0: 'INITIALISING',
1717
4: 'STOPPED',
1818
5: 'OPERATIONAL',
@@ -21,7 +21,7 @@
2121
127: 'PRE-OPERATIONAL'
2222
}
2323

24-
NMT_COMMANDS: Final[Dict[str, int]] = {
24+
NMT_COMMANDS: Final[dict[str, int]] = {
2525
'OPERATIONAL': 1,
2626
'STOPPED': 2,
2727
'SLEEP': 80,
@@ -32,7 +32,7 @@
3232
'RESET COMMUNICATION': 130
3333
}
3434

35-
COMMAND_TO_STATE: Final[Dict[int, int]] = {
35+
COMMAND_TO_STATE: Final[dict[int, int]] = {
3636
1: 5,
3737
2: 4,
3838
80: 80,
@@ -117,7 +117,7 @@ def __init__(self, node_id: int):
117117
#: Timestamp of last heartbeat message
118118
self.timestamp: Optional[float] = None
119119
self.state_update = threading.Condition()
120-
self._callbacks: List[Callable[[int], None]] = []
120+
self._callbacks: list[Callable[[int], None]] = []
121121

122122
def on_heartbeat(self, can_id, data, timestamp):
123123
with self.state_update:

canopen/node/local.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations
22

33
import logging
4-
from typing import Dict, Union
4+
from typing import Union
55

66
import canopen.network
77
from canopen import objectdictionary
@@ -25,7 +25,7 @@ def __init__(
2525
):
2626
super(LocalNode, self).__init__(node_id, object_dictionary)
2727

28-
self.data_store: Dict[int, Dict[int, bytes]] = {}
28+
self.data_store: dict[int, dict[int, bytes]] = {}
2929
self._read_callbacks = []
3030
self._write_callbacks = []
3131

canopen/objectdictionary/__init__.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
import logging
88
import struct
9-
from collections.abc import Mapping, MutableMapping
10-
from typing import Dict, Iterator, List, Optional, TextIO, Union
9+
from collections.abc import Iterator, Mapping, MutableMapping
10+
from typing import Optional, TextIO, Union
1111

1212
from canopen.objectdictionary.datatypes import *
1313
from canopen.objectdictionary.datatypes import IntegerN, UnsignedN
@@ -367,9 +367,9 @@ def __init__(self, name: str, index: int, subindex: int = 0):
367367
#: Description of variable
368368
self.description: str = ""
369369
#: Dictionary of value descriptions
370-
self.value_descriptions: Dict[int, str] = {}
370+
self.value_descriptions: dict[int, str] = {}
371371
#: Dictionary of bitfield definitions
372-
self.bit_definitions: Dict[str, List[int]] = {}
372+
self.bit_definitions: dict[str, list[int]] = {}
373373
#: Storage location of index
374374
self.storage_location = None
375375
#: Can this variable be mapped to a PDO

canopen/pdo/base.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
import logging
55
import math
66
import threading
7-
from collections.abc import Mapping
8-
from typing import Callable, Dict, Iterator, List, Optional, TYPE_CHECKING, Union
7+
from collections.abc import Iterator, Mapping
8+
from typing import Callable, Optional, TYPE_CHECKING, Union
99

1010
import canopen.network
1111
from canopen import objectdictionary
@@ -150,7 +150,7 @@ def __init__(self, com_offset, map_offset, pdo_node: PdoBase, cob_base=None):
150150
:param pdo_node:
151151
:param cob_base:
152152
"""
153-
self.maps: Dict[int, PdoMap] = {}
153+
self.maps: dict[int, PdoMap] = {}
154154
for map_no in range(512):
155155
if com_offset + map_no in pdo_node.node.object_dictionary:
156156
new_map = PdoMap(
@@ -196,7 +196,7 @@ def __init__(self, pdo_node, com_record, map_array):
196196
#: Ignores SYNC objects up to this SYNC counter value (optional)
197197
self.sync_start_value: Optional[int] = None
198198
#: List of variables mapped to this PDO
199-
self.map: List[PdoVariable] = []
199+
self.map: list[PdoVariable] = []
200200
self.length: int = 0
201201
#: Current message data
202202
self.data = bytearray()

canopen/profiles/p402.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# inspired by the NmtMaster code
22
import logging
33
import time
4-
from typing import Dict
54

65
from canopen.node import RemoteNode
76
from canopen.pdo import PdoMap
@@ -215,8 +214,8 @@ class BaseNode402(RemoteNode):
215214
def __init__(self, node_id, object_dictionary):
216215
super(BaseNode402, self).__init__(node_id, object_dictionary)
217216
self.tpdo_values = {} # { index: value from last received TPDO }
218-
self.tpdo_pointers: Dict[int, PdoMap] = {}
219-
self.rpdo_pointers: Dict[int, PdoMap] = {}
217+
self.tpdo_pointers: dict[int, PdoMap] = {}
218+
self.rpdo_pointers: dict[int, PdoMap] = {}
220219

221220
def setup_402_state_machine(self, read_pdos=True):
222221
"""Configure the state machine by searching for a TPDO that has the StatusWord mapped.

canopen/sdo/base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
from __future__ import annotations
22

33
import binascii
4-
from collections.abc import Mapping
5-
from typing import Iterator, Optional, Union
4+
from collections.abc import Iterator, Mapping
5+
from typing import Optional, Union
66

77
import canopen.network
88
from canopen import objectdictionary

pyproject.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[build-system]
2-
requires = ["setuptools>=69", "wheel", "setuptools_scm>=8"]
2+
requires = ["setuptools>=77", "wheel", "setuptools_scm>=8"]
33
build-backend = "setuptools.build_meta"
44

55
[project]
@@ -11,11 +11,11 @@ authors = [
1111
]
1212
description = "CANopen stack implementation"
1313
readme = "README.rst"
14-
requires-python = ">=3.8"
15-
license = {file = "LICENSE.txt"}
14+
requires-python = ">=3.9"
15+
license = "MIT"
16+
license-files = ["LICENSE.txt"]
1617
classifiers = [
1718
"Development Status :: 5 - Production/Stable",
18-
"License :: OSI Approved :: MIT License",
1919
"Operating System :: OS Independent",
2020
"Programming Language :: Python :: 3 :: Only",
2121
"Intended Audience :: Developers",
@@ -50,7 +50,7 @@ filterwarnings = [
5050
]
5151

5252
[tool.mypy]
53-
python_version = "3.8"
53+
python_version = "3.9"
5454
exclude = [
5555
"^examples*",
5656
"^test*",

0 commit comments

Comments
 (0)