Skip to content
Merged
6 changes: 3 additions & 3 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.11", "3.12", "3.13", "3.14"]
python-version: ["3.12", "3.13", "3.14"]

runs-on: ${{ matrix.os }}

Expand Down Expand Up @@ -122,7 +122,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.12", "3.13"] # TODO: add "3.14" once stable
python-version: ["3.12", "3.13"] # TODO: add "3.14" once https://github.com/prospector-dev/prospector/issues/825 fixed

steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -151,7 +151,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.12", "3.13", "3.14"]
python-version: ["3.12", "3.13", "3.14"]

steps:
- uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ version: 2
build:
os: ubuntu-22.04
tools:
python: "3.11"
python: "3.13"
apt_packages:
- graphviz

Expand Down
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[project]
name = "py-uds"
description = "UDS (Unified Diagnostic Services) protocol handler."
requires-python = ">=3.11"
requires-python = ">=3.12"
readme = "README.rst"
license = "MIT"
license-files = ["LICENSE"]
Expand All @@ -16,7 +16,6 @@ classifiers = [
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
DEFAULT_DIAGNOSTIC_MESSAGE_CONTINUATION,
AbstractConditionalDataRecord,
AbstractDataRecord,
AliasMessageStructure,
Callable,
ConditionalFormulaDataRecord,
ConditionalMappingDataRecord,
Mapping,
MessageStructureAlias,
Sequence,
)
from uds.utilities import InconsistencyError
Expand Down Expand Up @@ -531,7 +531,7 @@ class TestConditionalFormulaDataRecordIntegration:
"""Integration tests for `ConditionalFormulaDataRecord` class."""

def setup_class(self):
def continuation_length_formula(raw_value: int) -> AliasMessageStructure:
def continuation_length_formula(raw_value: int) -> MessageStructureAlias:
if raw_value <= 0 or raw_value > 20:
raise ValueError
return [RawDataRecord(name="Entries",
Expand Down
25 changes: 12 additions & 13 deletions tests/system_tests/abstract_system_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from abc import ABC
from threading import Timer
from time import sleep
from typing import List, Optional

from uds.message import UdsMessage, UdsMessageRecord
from uds.packet import AbstractPacket, AbstractPacketRecord
Expand All @@ -19,20 +18,20 @@ class BaseSystemTests(ABC):
TASK_TIMING_TOLERANCE: TimeMillisecondsAlias = 30
TIMESTAMP_TOLERANCE: TimeMillisecondsAlias = 1

sent_messages: List[UdsMessageRecord]
received_messages: List[UdsMessageRecord]
sent_packets: List[AbstractPacketRecord]
_timers: List[Timer]
sent_messages: list[UdsMessageRecord]
received_messages: list[UdsMessageRecord]
sent_packets: list[AbstractPacketRecord]
_timers: list[Timer]

def setup_method(self):
"""
Common setup:
- define common variables
"""
self.sent_messages: List[UdsMessageRecord] = []
self.received_messages: List[UdsMessageRecord] = []
self.sent_packets: List[AbstractPacketRecord] = []
self._timers: List[Timer] = []
self.sent_messages: list[UdsMessageRecord] = []
self.received_messages: list[UdsMessageRecord] = []
self.sent_packets: list[AbstractPacketRecord] = []
self._timers: list[Timer] = []

def teardown_method(self):
"""
Expand Down Expand Up @@ -77,7 +76,7 @@ def _send_packet():
async def async_send_packet(transport_interface: AbstractTransportInterface,
packet: AbstractPacket,
delay: TimeMillisecondsAlias,
loop: Optional[asyncio.AbstractEventLoop] = None) -> AbstractPacketRecord:
loop: None | asyncio.AbstractEventLoop = None) -> AbstractPacketRecord:
"""
Send a packet asynchronously over Transport Interface.

Expand All @@ -94,8 +93,8 @@ async def async_send_packet(transport_interface: AbstractTransportInterface,

def receive_message(self,
transport_interface: AbstractTransportInterface,
start_timeout: Optional[TimeMillisecondsAlias],
end_timeout: Optional[TimeMillisecondsAlias],
start_timeout: None | TimeMillisecondsAlias,
end_timeout: None | TimeMillisecondsAlias,
delay: TimeMillisecondsAlias) -> Timer:
"""
Receive UDS message over Transport Interface.
Expand Down Expand Up @@ -146,7 +145,7 @@ def _send_message():
async def async_send_message(transport_interface: AbstractTransportInterface,
message: UdsMessage,
delay: TimeMillisecondsAlias,
loop: Optional[asyncio.AbstractEventLoop] = None) -> UdsMessageRecord:
loop: None | asyncio.AbstractEventLoop = None) -> UdsMessageRecord:
"""
Send UDS message asynchronously over Transport Interface.

Expand Down
3 changes: 1 addition & 2 deletions tests/system_tests/can/python_can/python_can.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from abc import ABC, abstractmethod
from threading import Timer
from time import perf_counter, sleep
from typing import List

import pytest
from tests.system_tests import BaseSystemTests
Expand Down Expand Up @@ -36,7 +35,7 @@ class AbstractPythonCanTests(BaseSystemTests, ABC):

can_interface_1: Bus
can_interface_2: Bus
sent_packets: List[CanPacketRecord]
sent_packets: list[CanPacketRecord]

@abstractmethod
def _define_interfaces(self):
Expand Down
2 changes: 1 addition & 1 deletion uds/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@

import importlib
import sys
from typing import Sequence
from collections.abc import Sequence


def __getattr__(name: str) -> object: # noqa: vulture
Expand Down
7 changes: 4 additions & 3 deletions uds/addressing/abstract_addressing_information.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
__all__ = ["AbstractAddressingInformation"]

from abc import ABC, abstractmethod
from collections.abc import Mapping
from types import MappingProxyType
from typing import Any, Dict, Mapping, Optional
from typing import Any

from uds.utilities import ReassignmentError

Expand Down Expand Up @@ -140,11 +141,11 @@ def _validate_addressing_information(self) -> None:
"""Check whether the provided addressing information are valid."""

@abstractmethod
def validate_addressing_params(self, **addressing_params: Any) -> Dict[str, Any]:
def validate_addressing_params(self, **addressing_params: Any) -> dict[str, Any]:
"""Check whether the provided parameters are complete and correct."""

@abstractmethod
def is_input_packet(self, **frame_attributes: Any) -> Optional[AddressingType]: # noqa: vulture
def is_input_packet(self, **frame_attributes: Any) -> None | AddressingType: # noqa: vulture
"""
Check if a frame with provided attributes is an input packet for this UDS Entity.

Expand Down
46 changes: 23 additions & 23 deletions uds/can/addressing/abstract_addressing_information.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
__all__ = ["AbstractCanAddressingInformation", "CANAddressingParams"]

from abc import ABC, abstractmethod
from typing import Any, Optional, TypedDict
from typing import Any, TypedDict

from uds.addressing import AddressingType
from uds.addressing.abstract_addressing_information import AbstractAddressingInformation
Expand All @@ -17,9 +17,9 @@ class CANAddressingParams(TypedDict, total=True):
addressing_format: CanAddressingFormat
addressing_type: AddressingType
can_id: int
target_address: Optional[int]
source_address: Optional[int]
address_extension: Optional[int]
target_address: None | int
source_address: None | int
address_extension: None | int


class AbstractCanAddressingInformation(AbstractAddressingInformation, ABC):
Expand All @@ -29,17 +29,17 @@ class InputAIParams(TypedDict, total=False):
""":ref:`Addressing Information <knowledge-base-n-ai>` configuration parameters."""

can_id: int
target_address: Optional[int]
source_address: Optional[int]
address_extension: Optional[int]
target_address: None | int
source_address: None | int
address_extension: None | int

class CanIdAIParams(TypedDict, total=True):
"""ref:`Addressing Information <knowledge-base-n-ai>` parameters that are carried by CAN Identifier."""

addressing_type: Optional[AddressingType]
target_address: Optional[int]
source_address: Optional[int]
priority: Optional[int]
addressing_type: None | AddressingType
target_address: None | int
source_address: None | int
priority: None | int

class DataBytesAIParamsAlias(TypedDict, total=False):
"""Alias of :ref:`Addressing Information <knowledge-base-n-ai>` parameters encoded in data field."""
Expand All @@ -50,10 +50,10 @@ class DataBytesAIParamsAlias(TypedDict, total=False):
class DecodedAIParamsAlias(TypedDict, total=True):
"""Alias of :ref:`Addressing Information <knowledge-base-n-ai>` parameters encoded in CAN ID and data field."""

addressing_type: Optional[AddressingType]
target_address: Optional[int]
source_address: Optional[int]
address_extension: Optional[int]
addressing_type: None | AddressingType
target_address: None | int
source_address: None | int
address_extension: None | int

ADDRESSING_FORMAT: CanAddressingFormat
"""CAN Addressing Format used."""
Expand Down Expand Up @@ -84,10 +84,10 @@ def __init__(self,
def validate_addressing_params(cls, # type: ignore # pylint: disable=arguments-differ
addressing_type: AddressingType,
addressing_format: CanAddressingFormat,
can_id: Optional[int] = None,
target_address: Optional[int] = None,
source_address: Optional[int] = None,
address_extension: Optional[int] = None) -> CANAddressingParams:
can_id: None | int = None,
target_address: None | int = None,
source_address: None | int = None,
address_extension: None | int = None) -> CANAddressingParams:
"""
Validate Addressing Information parameters of a CAN packet.

Expand All @@ -108,7 +108,7 @@ def validate_addressing_params(cls, # type: ignore # pylint: disable=arguments
@staticmethod
@abstractmethod
def is_compatible_can_id(can_id: int,
addressing_type: Optional[AddressingType]) -> bool:
addressing_type: None | AddressingType) -> bool:
"""
Check whether provided CAN ID is consistent with this CAN Addressing Format.

Expand Down Expand Up @@ -163,8 +163,8 @@ def decode_frame_ai_params(cls, can_id: int, raw_frame_data: RawBytesAlias) -> D
@classmethod
@abstractmethod
def encode_ai_data_bytes(cls,
target_address: Optional[int] = None,
address_extension: Optional[int] = None) -> bytearray:
target_address: None | int = None,
address_extension: None | int = None) -> bytearray:
"""
Generate data bytes that carry Addressing Information.

Expand All @@ -177,7 +177,7 @@ def encode_ai_data_bytes(cls,
def is_input_packet(self, # type: ignore # pylint: disable=arguments-differ
can_id: int,
raw_frame_data: RawBytesAlias,
**_: Any) -> Optional[AddressingType]:
**_: Any) -> None | AddressingType:
"""
Check if a frame with provided attributes is an input packet for this UDS Entity.

Expand Down
18 changes: 8 additions & 10 deletions uds/can/addressing/addressing_information.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@

__all__ = ["CanAddressingInformation"]

from typing import Dict, Optional, Type

from uds.addressing import AddressingType
from uds.utilities import InconsistencyError, RawBytesAlias, validate_raw_bytes

Expand All @@ -22,7 +20,7 @@
class CanAddressingInformation:
"""CAN Entity (either server or client) Addressing Information."""

ADDRESSING_INFORMATION_MAPPING: Dict[CanAddressingFormat, Type[AbstractCanAddressingInformation]] = {
ADDRESSING_INFORMATION_MAPPING: dict[CanAddressingFormat, type[AbstractCanAddressingInformation]] = {
CanAddressingFormat.NORMAL_ADDRESSING: NormalCanAddressingInformation,
CanAddressingFormat.NORMAL_FIXED_ADDRESSING: NormalFixedCanAddressingInformation,
CanAddressingFormat.EXTENDED_ADDRESSING: ExtendedCanAddressingInformation,
Expand Down Expand Up @@ -93,10 +91,10 @@ def validate_ai_data_bytes(cls, addressing_format: CanAddressingFormat, ai_data_
def validate_addressing_params(cls,
addressing_format: CanAddressingFormat,
addressing_type: AddressingType,
can_id: Optional[int] = None,
target_address: Optional[int] = None,
source_address: Optional[int] = None,
address_extension: Optional[int] = None) -> CANAddressingParams:
can_id: None | int = None,
target_address: None | int = None,
source_address: None | int = None,
address_extension: None | int = None) -> CANAddressingParams:
"""
Validate Addressing Information parameters of a CAN packet.

Expand All @@ -122,7 +120,7 @@ def validate_addressing_params(cls,
def is_compatible_can_id(cls,
addressing_format: CanAddressingFormat,
can_id: int,
addressing_type: Optional[AddressingType] = None) -> bool:
addressing_type: None | AddressingType = None) -> bool:
"""
Check whether provided CAN ID is consistent the provided CAN Addressing Format.

Expand Down Expand Up @@ -223,8 +221,8 @@ def encode_can_id(cls,
@classmethod
def encode_ai_data_bytes(cls,
addressing_format: CanAddressingFormat,
target_address: Optional[int] = None,
address_extension: Optional[int] = None) -> bytearray:
target_address: None | int = None,
address_extension: None | int = None) -> bytearray:
"""
Generate data bytes that carry Addressing Information.

Expand Down
15 changes: 7 additions & 8 deletions uds/can/addressing/extended_addressing.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

__all__ = ["ExtendedCanAddressingInformation"]

from typing import Optional

from uds.addressing import AddressingType
from uds.can.addressing.abstract_addressing_information import AbstractCanAddressingInformation, CANAddressingParams
Expand Down Expand Up @@ -38,10 +37,10 @@ def _validate_addressing_information(self) -> None:
def validate_addressing_params(cls, # type: ignore
addressing_type: AddressingType,
addressing_format: CanAddressingFormat = ADDRESSING_FORMAT,
can_id: Optional[int] = None,
target_address: Optional[int] = None,
source_address: Optional[int] = None,
address_extension: Optional[int] = None) -> CANAddressingParams:
can_id: None | int = None,
target_address: None | int = None,
source_address: None | int = None,
address_extension: None | int = None) -> CANAddressingParams:
"""
Validate Addressing Information parameters of a CAN packet that uses Extended Addressing format.

Expand Down Expand Up @@ -77,7 +76,7 @@ def validate_addressing_params(cls, # type: ignore

@staticmethod
def is_compatible_can_id(can_id: int,
addressing_type: Optional[AddressingType] = None) -> bool:
addressing_type: None | AddressingType = None) -> bool:
"""
Check whether provided CAN ID is consistent with Extended Addressing format.

Expand Down Expand Up @@ -112,8 +111,8 @@ def decode_data_bytes_ai_params(

@classmethod
def encode_ai_data_bytes(cls,
target_address: Optional[int] = None,
address_extension: Optional[int] = None) -> bytearray:
target_address: None | int = None,
address_extension: None | int = None) -> bytearray:
"""
Generate data bytes that carry Addressing Information.

Expand Down
Loading
Loading