Skip to content
This repository was archived by the owner on Jul 24, 2025. It is now read-only.
Draft
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ['3.12']
python-version: ['3.10', '3.11', '3.12']
os: [ubuntu-latest, macOS-latest, windows-latest]

steps:
Expand All @@ -27,4 +27,4 @@ jobs:
python -m pip install -e .[test]
- name: Run tests
run: |
python -m pytest tests
python -m pytest -v tests
18 changes: 11 additions & 7 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ permissions:
jobs:
lint:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
python-version: ['3.10', '3.11', '3.12']

steps:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
- name: Set up Python 3.12
uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d
with:
python-version: 3.12
- name: Run pre-commit
uses: pre-commit/action@646c83fcd040023954eafda54b4db0192ce70507
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
- name: 'Set up Python ${{ matrix.python-version }}'
uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d
with:
python-version: '${{ matrix.python-version }}'
- name: Run pre-commit
uses: pre-commit/action@646c83fcd040023954eafda54b4db0192ce70507
9 changes: 6 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "aws-sdk-signers"
requires-python = ">=3.12"
requires-python = ">=3.10"
authors = [
{name = "Amazon Web Services"},
]
Expand All @@ -14,10 +14,15 @@ license = {file = "LICENSE"}
keywords = ["AWS", "Signing", "SigV4", "HTTP"]
classifiers = [
"Development Status :: 2 - Pre-Alpha",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python"
]
dynamic = ["version"]
dependencies = [
'typing-extensions >=4.12.2,<5 ; python_version == "3.10"',
]

[project.optional-dependencies]
test = [
Expand All @@ -38,8 +43,6 @@ path = "src/aws_sdk_signers/_version.py"
line-length = 88
indent-width = 4

target-version = "py312"

[tool.ruff.lint]
select = ["E4", "E7", "E9", "F", "I", "UP"]
ignore = []
Expand Down
8 changes: 7 additions & 1 deletion src/aws_sdk_signers/_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

from __future__ import annotations

import sys
from collections import Counter, OrderedDict
from collections.abc import AsyncIterable, Iterable, Iterator
from copy import deepcopy
Expand Down Expand Up @@ -210,7 +211,12 @@ def __len__(self) -> int:
return len(self.entries)

def __repr__(self) -> str:
return f"Fields({self.entries})"
if sys.version_info < (3, 12):
# Dicts are ordered in 3.11 and 3.10, so it's safe to use dict.
# Used to mock a 3.12-like output.
return f"Fields(OrderedDict({dict(self.entries)}))"
else:
return f"Fields({self.entries})"

def __contains__(self, key: str) -> bool:
return self._normalize_field_name(key) in self.entries
Expand Down
10 changes: 9 additions & 1 deletion src/aws_sdk_signers/_identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,19 @@
SPDX-License-Identifier: Apache-2.0
"""

import sys
from dataclasses import dataclass
from datetime import UTC, datetime
from datetime import datetime, timezone

from .interfaces.identity import Identity

if sys.version_info < (3, 12):
from datetime import timezone

UTC = timezone.utc
else:
from datetime import UTC


@dataclass(kw_only=True)
class AWSCredentialIdentity(Identity):
Expand Down
11 changes: 7 additions & 4 deletions src/aws_sdk_signers/_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
SPDX-License-Identifier: Apache-2.0
"""

import sys
from asyncio import iscoroutinefunction
from collections.abc import AsyncIterable, AsyncIterator, Awaitable, Callable
from io import BytesIO
from typing import (
Self,
cast,
)
from typing import cast

if sys.version_info < (3, 11):
from typing_extensions import Self
else:
from typing import Self

from aws_sdk_signers.interfaces.io import AsyncByteStream, ByteStream

Expand Down
19 changes: 16 additions & 3 deletions src/aws_sdk_signers/signers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,31 @@
import datetime
import hmac
import io
import sys
import warnings
from collections.abc import AsyncIterable
from copy import deepcopy
from hashlib import sha256
from typing import Required, TypedDict
from typing import TypedDict
from urllib.parse import parse_qsl, quote

if sys.version_info < (3, 11):
from typing_extensions import Required
else:
from typing import Required

from ._http import URI, AWSRequest, Field
from ._identity import AWSCredentialIdentity
from ._io import AsyncBytesReader
from .exceptions import AWSSDKWarning, MissingExpectedParameterException

if sys.version_info < (3, 12):
from datetime import timezone

UTC = timezone.utc
else:
from datetime import UTC

HEADERS_EXCLUDED_FROM_SIGNING: tuple[str, ...] = (
"accept",
"accept-encoding",
Expand Down Expand Up @@ -185,7 +198,7 @@ def _generate_new_request(self, *, request: AWSRequest) -> AWSRequest:

def _resolve_signing_date(self, *, date: str | None) -> str:
if date is None:
date_obj = datetime.datetime.now(datetime.UTC)
date_obj = datetime.datetime.now(UTC)
date = date_obj.strftime(SIGV4_TIMESTAMP_FORMAT)
return date

Expand Down Expand Up @@ -548,7 +561,7 @@ async def _generate_new_request(self, *, request: AWSRequest) -> AWSRequest:

async def _resolve_signing_date(self, *, date: str | None) -> str:
if date is None:
date_obj = datetime.datetime.now(datetime.UTC)
date_obj = datetime.datetime.now(UTC)
date = date_obj.strftime(SIGV4_TIMESTAMP_FORMAT)
return date

Expand Down
10 changes: 9 additions & 1 deletion tests/unit/auth/test_sigv4.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import os
import pathlib
import re
import sys
from collections.abc import Iterable
from datetime import UTC, datetime
from datetime import datetime
from http.server import BaseHTTPRequestHandler
from io import BytesIO

Expand All @@ -24,6 +25,13 @@
)
from freezegun import freeze_time

if sys.version_info < (3, 12):
from datetime import timezone

UTC = timezone.utc
else:
from datetime import UTC

SECRET_KEY: str = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"
ACCESS_KEY: str = "AKIDEXAMPLE"
SERVICE: str = "service"
Expand Down
10 changes: 9 additions & 1 deletion tests/unit/test_identity.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
from datetime import UTC, datetime, timedelta
import sys
from datetime import datetime, timedelta

import pytest
from aws_sdk_signers import AWSCredentialIdentity

if sys.version_info < (3, 12):
from datetime import timezone

UTC = timezone.utc
else:
from datetime import UTC


@pytest.mark.parametrize(
"access_key_id,secret_access_key,session_token,expiration",
Expand Down
10 changes: 9 additions & 1 deletion tests/unit/test_signers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
"""

import re
import sys
import typing
from datetime import UTC, datetime
from datetime import datetime
from io import BytesIO

import pytest
Expand All @@ -19,6 +20,13 @@
SigV4SigningProperties,
)

if sys.version_info < (3, 12):
from datetime import timezone

UTC = timezone.utc
else:
from datetime import UTC

SIGV4_RE = re.compile(
r"AWS4-HMAC-SHA256 "
r"Credential=(?P<access_key>\w+)/\d+/"
Expand Down