Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
with:
# use the minimum py ver we support to
# generate the wheel.
python-version: "3.10"
python-version: "3.8"

- name: Install build deps
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ jobs:
strategy:
fail-fast: true
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- name: Checkout commit
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
Expand Down
2 changes: 1 addition & 1 deletion .python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.10
3.8
2 changes: 1 addition & 1 deletion DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This document describes how to set up a development environment for otaclient-io

## Prerequisites

- Python 3.10 or later
- Python 3.8 or later
- [uv](https://docs.astral.sh/uv/getting-started/installation/) package manager

## Setup
Expand Down
6 changes: 4 additions & 2 deletions proto/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ requires = [
name = "otaclient-iot-logging-server-pb2"
version = "1.0.0"
readme = "README.md"
requires-python = ">=3.10"
requires-python = ">=3.8"
classifiers = [
"License :: OSI Approved :: Apache Software License",
"Operating System :: Unix",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
Expand Down Expand Up @@ -80,4 +82,4 @@ ignore = [
"**/*_pb2.py*",
"**/*_pb2_grpc.py*",
]
pythonVersion = "3.10"
pythonVersion = "3.8"
6 changes: 4 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ license = "Apache-2.0"
license-files = [
"LICENSE",
]
requires-python = ">=3.10"
requires-python = ">=3.8"
classifiers = [
"License :: OSI Approved :: Apache Software License",
"Operating System :: Unix",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
Expand Down Expand Up @@ -74,7 +76,7 @@ sources = [
source = "vcs"

[tool.ruff]
target-version = "py310"
target-version = "py38"
# NOTE: not include tests and tools for now
include = [
"pyproject.toml",
Expand Down
99 changes: 78 additions & 21 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,66 +1,119 @@
# This file was autogenerated by uv via the following command:
# uv export --frozen --no-dev --no-editable --no-hashes --no-emit-project
aiohappyeyeballs==2.6.1
aiohappyeyeballs==2.4.4 ; python_full_version < '3.9'
# via aiohttp
aiohttp==3.13.3
aiohappyeyeballs==2.6.1 ; python_full_version >= '3.9'
# via aiohttp
aiohttp==3.10.11 ; python_full_version < '3.9'
# via otaclient-iot-logging-server
aiohttp==3.13.3 ; python_full_version >= '3.9'
# via otaclient-iot-logging-server
aiosignal==1.4.0
aiosignal==1.3.1 ; python_full_version < '3.9'
# via aiohttp
aiosignal==1.4.0 ; python_full_version >= '3.9'
# via aiohttp
annotated-types==0.7.0
# via pydantic
async-timeout==5.0.1 ; python_full_version < '3.11'
# via aiohttp
attrs==25.4.0
attrs==25.3.0 ; python_full_version < '3.9'
# via aiohttp
attrs==25.4.0 ; python_full_version >= '3.9'
# via aiohttp
awscrt==0.31.2
# via otaclient-iot-logging-server
boto3==1.42.49
boto3==1.37.38 ; python_full_version < '3.9'
# via otaclient-iot-logging-server
boto3==1.42.49 ; python_full_version >= '3.9'
# via otaclient-iot-logging-server
botocore==1.42.49
botocore==1.37.38 ; python_full_version < '3.9'
# via
# boto3
# s3transfer
frozenlist==1.8.0
botocore==1.42.49 ; python_full_version >= '3.9'
# via
# boto3
# s3transfer
frozenlist==1.5.0 ; python_full_version < '3.9'
# via
# aiohttp
# aiosignal
frozenlist==1.8.0 ; python_full_version >= '3.9'
# via
# aiohttp
# aiosignal
grpcio==1.78.0
grpcio==1.70.0 ; python_full_version < '3.9'
# via otaclient-iot-logging-server
grpcio==1.78.0 ; python_full_version >= '3.9'
# via otaclient-iot-logging-server
idna==3.11
# via yarl
jmespath==1.1.0
jmespath==1.0.1 ; python_full_version < '3.9'
# via
# boto3
# botocore
multidict==6.7.1
jmespath==1.1.0 ; python_full_version >= '3.9'
# via
# boto3
# botocore
multidict==6.1.0 ; python_full_version < '3.9'
# via
# aiohttp
# yarl
propcache==0.4.1
multidict==6.7.1 ; python_full_version >= '3.9'
# via
# aiohttp
# yarl
protobuf==6.33.5
propcache==0.2.0 ; python_full_version < '3.9'
# via yarl
propcache==0.4.1 ; python_full_version >= '3.9'
# via
# aiohttp
# yarl
protobuf==5.29.6 ; python_full_version < '3.9'
# via otaclient-iot-logging-server
protobuf==6.33.5 ; python_full_version >= '3.9'
# via otaclient-iot-logging-server
pydantic==2.12.5
pydantic==2.10.6 ; python_full_version < '3.9'
# via
# otaclient-iot-logging-server
# pydantic-settings
pydantic==2.12.5 ; python_full_version >= '3.9'
# via
# otaclient-iot-logging-server
# pydantic-settings
pydantic-core==2.41.5
pydantic-core==2.27.2 ; python_full_version < '3.9'
# via pydantic
pydantic-settings==2.13.0
pydantic-core==2.41.5 ; python_full_version >= '3.9'
# via pydantic
pydantic-settings==2.8.1 ; python_full_version < '3.9'
# via otaclient-iot-logging-server
pydantic-settings==2.11.0 ; python_full_version == '3.9.*'
# via otaclient-iot-logging-server
pydantic-settings==2.13.0 ; python_full_version >= '3.10'
# via otaclient-iot-logging-server
python-dateutil==2.9.0.post0
# via botocore
python-dotenv==1.2.1
python-dotenv==1.0.1 ; python_full_version < '3.9'
# via pydantic-settings
python-dotenv==1.2.1 ; python_full_version >= '3.9'
# via pydantic-settings
pyyaml==6.0.3
# via otaclient-iot-logging-server
s3transfer==0.16.0
s3transfer==0.11.5 ; python_full_version < '3.9'
# via boto3
s3transfer==0.16.0 ; python_full_version >= '3.9'
# via boto3
six==1.17.0
# via python-dateutil
typing-extensions==4.15.0
typing-extensions==4.13.2 ; python_full_version < '3.9'
# via
# annotated-types
# multidict
# otaclient-iot-logging-server
# pydantic
# pydantic-core
typing-extensions==4.15.0 ; python_full_version >= '3.9'
# via
# aiosignal
# grpcio
Expand All @@ -69,11 +122,15 @@ typing-extensions==4.15.0
# pydantic
# pydantic-core
# typing-inspection
typing-inspection==0.4.2
typing-inspection==0.4.2 ; python_full_version >= '3.9'
# via
# pydantic
# pydantic-settings
urllib3==2.6.3
urllib3==1.26.20 ; python_full_version < '3.10'
# via botocore
yarl==1.22.0
urllib3==2.6.3 ; python_full_version >= '3.10'
# via botocore
yarl==1.15.2 ; python_full_version < '3.9'
# via aiohttp
yarl==1.22.0 ; python_full_version >= '3.9'
# via aiohttp
2 changes: 1 addition & 1 deletion sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ sonar.python.coverage.reportPaths=test_result/coverage.xml
sonar.sources=./src
sonar.tests=tests
sonar.sourceEncoding=UTF-8
sonar.python.version=3.10,3.11,3.12,3.13
sonar.python.version=3.8,3.9,3.10,3.11,3.12,3.13
# Wait for quality gate result and fail if not passed
sonar.qualitygate.wait=true
4 changes: 2 additions & 2 deletions src/otaclient_iot_logging_server/_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@

from enum import Enum
from queue import Queue
from typing import Literal, TypeAlias, TypedDict
from typing import Literal, TypedDict

from typing_extensions import NotRequired
from typing_extensions import NotRequired, TypeAlias

# LogQueue is a queue of LogGroupType, ecu_id, and LogMessage
LogsQueue: TypeAlias = "Queue[tuple[LogGroupType, str, LogMessage]]"
Expand Down
12 changes: 10 additions & 2 deletions src/otaclient_iot_logging_server/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@

import time
from functools import partial, wraps
from typing import Any, Callable, ParamSpec, TypeAlias, TypeVar, overload
from typing import Any, Callable, Optional, TypeVar, overload

from pydantic import BaseModel, ConfigDict
from typing_extensions import ParamSpec, TypeAlias

from otaclient_iot_logging_server._common import PKCS11URI

Expand Down Expand Up @@ -95,7 +96,7 @@ def retry(


def retry(
func: Callable[P, RT] | None = None,
func: Optional[Callable[P, RT]] = None,
/,
backoff_factor: float = 0.1,
backoff_max: int = 6,
Expand Down Expand Up @@ -129,6 +130,13 @@ def _inner(*args: P.args, **kwargs: P.kwargs) -> RT:
return _inner


def remove_prefix(_str: str, _prefix: str) -> str:
# NOTE: in py3.8 we don't have str.removeprefix yet.
if _str.startswith(_prefix):
return _str.replace(_prefix, "", 1)
return _str


def parse_pkcs11_uri(_pkcs11_uri: str) -> PKCS11URI:
_, pkcs11_opts_str = _pkcs11_uri.split(":", maxsplit=1)
pkcs11_opts_dict: dict[str, Any] = {}
Expand Down
18 changes: 9 additions & 9 deletions src/otaclient_iot_logging_server/boto3_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import subprocess
from http import HTTPStatus
from pathlib import Path
from typing import Any
from typing import Any, Dict, Optional

from awscrt.http import HttpClientConnection, HttpRequest
from awscrt.io import (
Expand Down Expand Up @@ -55,7 +55,7 @@ def _load_pkcs11_cert(
pkcs11_lib: str,
slot_id: str,
private_key_label: str,
user_pin: str | None = None,
user_pin: Optional[str] = None,
) -> bytes:
"""Load certificate from a pkcs11 interface(backed by a TPM2.0 chip).

Expand Down Expand Up @@ -85,7 +85,7 @@ def _convert_to_pem(_data: bytes) -> bytes:
return ssl.DER_cert_to_PEM_cert(_data).encode()


def _load_certificate(cert_path: str, pkcs11_cfg: PKCS11Config | None) -> bytes:
def _load_certificate(cert_path: str, pkcs11_cfg: Optional[PKCS11Config]) -> bytes:
"""
NOTE: Only PEM format cert is supported.
"""
Expand Down Expand Up @@ -118,7 +118,7 @@ def _parse_credentials_response(
response_status: int,
response_body: bytes,
credential_url: str,
) -> dict[str, Any]:
) -> Dict[str, Any]:
"""Parse credential provider response, raising on non-200 status."""
if response_status != HTTPStatus.OK:
logger.error(
Expand Down Expand Up @@ -154,7 +154,7 @@ def _build_tls_context_from_path(
def _build_tls_context_pkcs11(
cert_pem: bytes,
pkcs11_cfg: PKCS11Config,
private_key_label: str | None = None,
private_key_label: Optional[str] = None,
) -> TlsContextOptions:
"""Build TLS context options using PKCS#11 for private key operations."""
return TlsContextOptions.create_client_with_mtls_pkcs11(
Expand All @@ -173,7 +173,7 @@ def _fetch_iot_credentials_via_awscrt(
role_alias: str,
thing_name: str,
tls_ctx_opt: TlsContextOptions,
) -> dict[str, Any]:
) -> Dict[str, Any]:
"""Fetch IAM credentials from AWS IoT Core Credential Provider via mTLS.

Uses awscrt for the HTTP request with the given TLS context.
Expand Down Expand Up @@ -234,7 +234,7 @@ def _fetch_iot_credentials(
thing_name: str,
cert_path: str,
key_path: str,
) -> dict[str, Any]:
) -> Dict[str, Any]:
"""Fetch IAM credentials using plain certificate/key files."""
tls_ctx_opt = _build_tls_context_from_path(cert_path, key_path)
return _fetch_iot_credentials_via_awscrt(
Expand All @@ -251,8 +251,8 @@ def _fetch_iot_credentials_pkcs11(
thing_name: str,
cert_pem: bytes,
pkcs11_cfg: PKCS11Config,
private_key_label: str | None = None,
) -> dict[str, Any]:
private_key_label: Optional[str] = None,
) -> Dict[str, Any]:
"""Fetch IAM credentials using PKCS#11 for private key operations."""
tls_ctx_opt = _build_tls_context_pkcs11(cert_pem, pkcs11_cfg, private_key_label)
return _fetch_iot_credentials_via_awscrt(
Expand Down
5 changes: 3 additions & 2 deletions src/otaclient_iot_logging_server/configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@
from __future__ import annotations

from pathlib import Path
from typing import Annotated, Literal
from typing import List, Literal

import yaml
from pydantic import BaseModel, BeforeValidator, Field, RootModel
from pydantic_settings import BaseSettings, SettingsConfigDict
from typing_extensions import Annotated

from otaclient_iot_logging_server.config_file_monitor import monitored_config_files

Expand Down Expand Up @@ -64,7 +65,7 @@ class _AWSProfile(BaseModel):
credential_endpoint: str


class AWSProfileInfo(RootModel[list[_AWSProfile]]):
class AWSProfileInfo(RootModel[List[_AWSProfile]]):
def get_profile_info(self, profile_name: str) -> _AWSProfile:
for profile in self.root:
if profile.profile_name == profile_name:
Expand Down
Loading
Loading