Skip to content

Commit 0311a1e

Browse files
committed
Make boto3 and warrant-lite optional dependencies for Nexity auth
These heavy dependencies (~100MB installed) are only used by NexityAuthStrategy. Moving them to a `nexity` extra avoids pulling in the entire AWS SDK for users who don't need Nexity support.
1 parent 279892c commit 0311a1e

6 files changed

Lines changed: 70 additions & 18 deletions

File tree

docs/getting-started.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,22 @@ uv add pyoverkiz
2525
pip install pyoverkiz
2626
```
2727

28+
### Optional extras
29+
30+
Some servers require additional dependencies that are not installed by default:
31+
32+
| Extra | Server | Packages |
33+
|-------|--------|----------|
34+
| `nexity` | Nexity | boto3, warrant-lite |
35+
36+
Install an extra with:
37+
38+
```bash
39+
uv add "pyoverkiz[nexity]"
40+
# or
41+
pip install "pyoverkiz[nexity]"
42+
```
43+
2844
## Choose your server
2945

3046
Use a cloud server when you want to connect through the vendor’s public API. Use a local server when you want LAN access to a gateway.

docs/migration-v2.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,4 +297,4 @@ These are not breaking, but worth knowing about when migrating:
297297
- **Device helpers**`Device.get_command_definition()` for looking up command metadata.
298298
- **Reference endpoints** — query server metadata: `get_reference_ui_classes()`, `get_reference_ui_widgets()`, `get_reference_ui_profile()`, `get_reference_controllable_types()`, etc.
299299
- **Firmware management**`get_devices_not_up_to_date()`, `get_device_firmware_status()`, `update_device_firmware()`.
300-
- **boto3 lazy import**`boto3` is only imported when the Nexity auth strategy is actually used.
300+
- **Optional Nexity dependencies**`boto3` and `warrant-lite` are no longer installed by default. Install them with `pip install pyoverkiz[nexity]` if you use the Nexity server. A clear `ImportError` is raised at login time if the extra is missing.

pyoverkiz/auth/strategies.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@
99
import ssl
1010
from collections.abc import Mapping
1111
from http import HTTPStatus
12-
from typing import TYPE_CHECKING, Any, cast
13-
14-
if TYPE_CHECKING:
15-
from botocore.client import BaseClient
12+
from typing import Any, cast
1613

1714
from aiohttp import ClientSession, FormData
1815

@@ -246,14 +243,20 @@ class NexityAuthStrategy(SessionLoginStrategy):
246243

247244
async def login(self) -> None:
248245
"""Perform login using Nexity username and password."""
249-
import boto3
250-
from botocore.config import Config
251-
from botocore.exceptions import ClientError
252-
from warrant_lite import WarrantLite
246+
try:
247+
import boto3
248+
from botocore.config import Config
249+
from botocore.exceptions import ClientError
250+
from warrant_lite import WarrantLite
251+
except ImportError as err:
252+
raise ImportError(
253+
"Nexity authentication requires the 'nexity' extra. "
254+
"Install it with: pip install pyoverkiz[nexity]"
255+
) from err
253256

254257
loop = asyncio.get_running_loop()
255258

256-
def _client() -> BaseClient:
259+
def _client() -> Any:
257260
return boto3.client(
258261
"cognito-idp", config=Config(region_name=NEXITY_COGNITO_REGION)
259262
)

pyproject.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@ dependencies = [
1717
"aiohttp<4.0.0,>=3.10.3",
1818
"backoff<3.0,>=1.10.0",
1919
"attrs>=21.2",
20-
"boto3<2.0.0,>=1.18.59",
21-
"warrant-lite<2.0.0,>=1.0.4",
2220
"cattrs>=23.2",
2321
]
2422

2523
[project.optional-dependencies]
24+
nexity = [
25+
"boto3<2.0.0,>=1.18.59",
26+
"warrant-lite<2.0.0,>=1.0.4",
27+
]
2628
docs = [
2729
"mkdocs>=1.5.0,<2.0",
2830
"mkdocs-material>=9.5.0",

tests/test_auth.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,13 @@
1313

1414
import pytest
1515
from aiohttp import ClientSession
16-
from botocore.exceptions import ClientError
16+
17+
try:
18+
from botocore.exceptions import ClientError
19+
20+
HAS_NEXITY_DEPS = True
21+
except ImportError:
22+
HAS_NEXITY_DEPS = False
1723

1824
from pyoverkiz.auth.base import AuthContext
1925
from pyoverkiz.auth.credentials import (
@@ -499,6 +505,28 @@ def test_boto3_not_imported_at_module_load(self):
499505
sys.modules[mod] = value
500506

501507
@pytest.mark.asyncio
508+
async def test_login_raises_import_error_without_nexity_extra(self):
509+
"""Login raises ImportError with install hint when nexity extra is missing."""
510+
server_config = ServerConfig(
511+
server=Server.NEXITY,
512+
name="Nexity",
513+
endpoint="https://api.nexity.com",
514+
manufacturer="Nexity",
515+
api_type=APIType.CLOUD,
516+
)
517+
credentials = UsernamePasswordCredentials("user", "pass")
518+
session = AsyncMock(spec=ClientSession)
519+
520+
strategy = NexityAuthStrategy(credentials, session, server_config, True)
521+
522+
with (
523+
patch.dict(sys.modules, {"boto3": None}),
524+
pytest.raises(ImportError, match="pyoverkiz\\[nexity\\]"),
525+
):
526+
await strategy.login()
527+
528+
@pytest.mark.asyncio
529+
@pytest.mark.skipif(not HAS_NEXITY_DEPS, reason="nexity extra not installed")
502530
async def test_login_maps_invalid_credentials_client_error(self):
503531
"""Map Cognito bad-credential errors to NexityBadCredentialsError."""
504532
server_config = ServerConfig(
@@ -527,6 +555,7 @@ async def test_login_maps_invalid_credentials_client_error(self):
527555
await strategy.login()
528556

529557
@pytest.mark.asyncio
558+
@pytest.mark.skipif(not HAS_NEXITY_DEPS, reason="nexity extra not installed")
530559
async def test_login_propagates_non_auth_client_error(self):
531560
"""Propagate non-auth Cognito errors to preserve failure context."""
532561
server_config = ServerConfig(

uv.lock

Lines changed: 7 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)